多路复用这种技术最早是在信息论领域提出的。多路复用是指在一条物理信道上传播多种信号,每种信号的频率或者频段不一样,多路复用技术将一条物理信道划分成多个频道,每个频道传播一个频段的信号。这样多个频率或者频段的信号可以在一条物理信道上进行传播。主要目的是为了有效地利用带宽。
多路复用通常分为频分多路复用、时分多路复用、波分多路复用、码分多址和空分多址。
多路复用在软件编程领域的应用
在软件编程领域,多路复用技术也被用到实际编程中。比如我们需要同时处理来自控制台的标准I/O输入输出,来自文件的读写,管道的读写等。比如我们需要同时处理来自多个客户端的I/O操作,比如我们需要同时处理大量来自客户端程序的I/O请求。还有比如我们需要同时处理来自多个数据流/管道的数据处理,这种应用场景需求在目前大数据领域是非常普遍,很多大数据平台技术都有应用到这种技术的。
I/O多路复用
I/O多路复用的实现
I/O多路复用的实现比较典型包括Select、Poll、以及Epoll。
Select、Poll、以及Epoll的实现和应用我在另外几篇文章都有详细讲解。
为什么叫“I/O多路复用”呢?
I/O多路复用主要提供了一种机制来同时处理来自多个地方的I/O请求。类似像Select、Poll以及Epoll这些实现,它们都是基于一种I/O事件处理机制来同时处理多个fd的I/O事件,进而基于事件驱动来处理相关fd的I/O请求。
kqueue
int kqueue(void);
该系统调用提供了一种当kevent内核事件发生或者条件存在时通知用户的基于一种称为过滤器(一小段内核代码)的结果的通用机制.
内核事件kevent通过一个(ident, filter)对来标识表示。
我通过man kqueue查看帮助文档时还看到,unix后面把可选的udata value也作为标识内核事件kevent的一个要素,即kevent通过一个(ident, filter, and optional udata value)元组来标识表示,这里的叫法也不一样了,这里是一个三元组,当然上面的(ident, filter)对也是一个元组,上面是一个二元组。udata value作为标识内核事件kevent的一个要素是可选的,只有kevent的flags指定了EV_UDATA_SPECIFIC,udata value才被认为是元组的一部分,即是一个三元组,udata value也作为标识内核事件kevent的一个要素。否则的话,udata value不被认为是元组的一部分,即还是一个三元组,和上面的一样。
kqueue的事件触发时机和linux下epoll的水平触发类似,事件注册后,只要读缓存中有数据可读,就会触发EVFILT_READ读事件,只要写缓存可写,就会触发EVFILT_WRITE写事件。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/event.h> typedef struct { char *ptr; unsigned int size; unsigned int offset; } buffer_t; #define BUFFER_INITIAL_DEFAULT_SIZE 1024 int buffer_alloc(buffer_t *buf, int size) { if (! buf) { return -1; } if (size <= 0) { size = BUFFER_INITIAL_DEFAULT_SIZE; } buf->size = size; buf->ptr = (char *) malloc(buf->size); if (! buf->ptr) { return -1; } buf->offset = 0; return 0; } int main(int argc, char **argv) { int kq_fd; struct kevent event, *eventlist; int nevents = 100; int fd; struct sockaddr_in sock_addr; int reuse = 1; buffer_t *r_buf; kq_fd = kqueue(); if (kq_fd == -1) { printf("kqueue err=%d!\n", errno); return -1; } fd = socket(AF_INET, SOCK_STREAM, 0); if (fd == -1) { printf("socket err=%d\n", errno); return -1; } bzero(&sock_addr, sizeof(struct sockaddr_in)); sock_addr.sin_family = AF_INET; sock_addr.sin_addr.s_addr = htonl(INADDR_ANY); sock_addr.sin_port = htons(3333); if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1) { printf("setsockopt err=%d, attempting to set SO_REUSEADDR option for sock=%d\n", errno, fd); return -1; } if (bind(fd, (struct sockaddr *) &sock_addr, sizeof(struct sockaddr_in)) == -1) { printf("bind err=%d\n", errno); return -1; } if (listen(fd, 50) == -1) { printf("listen err=%d\n", errno); return -1; } r_buf = malloc(sizeof(buffer_t)); buffer_alloc(r_buf, 0); event.ident = fd; event.filter = EVFILT_READ; event.flags = EV_ADD | EV_ENABLE; event.fflags = 0; event.data = 0; event.udata = r_buf; // just register, do not return pending events. // and, only resiter one event by one call, but not pass // an array of event to changelist to register number of // events by one call. if (kevent(kq_fd, &event, 1, NULL, 0, NULL) == -1) { printf("kevent err=%d, while register events.\n", errno); return -1; } eventlist = malloc(sizeof(struct kevent) * nevents); if (eventlist == NULL) { printf("alloc err!\n"); return -1; } while (1) { int i; // just return pending events, do not register event. int nretevents = kevent(kq_fd, NULL, 0, eventlist, nevents, NULL); if (nretevents == -1) { printf("kevent err=%d\n", errno); return -1; } printf("kevent, return %d\n", nretevents); for (i = 0; i < nretevents; i++) { struct kevent *e = &eventlist[i]; buffer_t *r_buf = e->udata; printf("%d: ident=%lu, filter=%d, flags=%u, fflags=%u\n", i, e->ident, e->filter, e->flags, e->fflags); if (e->filter == EVFILT_READ) { if (e->ident == fd) { // accept int fd_get; struct sockaddr_in sockaddr; char *ip; socklen_t sockaddr_len = sizeof(struct sockaddr_in); fd_get = accept(fd, (struct sockaddr *) &sockaddr, &sockaddr_len); if (fd_get == -1) { printf("accept err=%d\n", errno); } else { buffer_t *r_buf; ip = inet_ntoa(sockaddr.sin_addr); printf("accept, ip=%s\n", ip); r_buf = malloc(sizeof(buffer_t)); if (! r_buf) { printf("alloc err!\n"); return -1; } if (buffer_alloc(r_buf, 0)) { printf("buffer alloc err!\n"); return -1; } event.ident = fd_get; event.udata = r_buf; // just register, do not return pending events. // and, only resiter one event by one call, but not pass // an array of event to changelist to register number of // events by one call. if (kevent(kq_fd, &event, 1, NULL, 0, NULL) == -1) { printf("kevent err=%d, while register events.\n", errno); return -1; } } } else { // read if (r_buf->offset >= r_buf->size) { int size = r_buf->size + BUFFER_INITIAL_DEFAULT_SIZE; r_buf->ptr = realloc(r_buf->ptr, size); if (! r_buf->ptr) { printf("alloc err!\n"); } else { r_buf->size = size; } } if (r_buf->offset < r_buf->size) { ssize_t nbytes; nbytes = read(e->ident, r_buf->ptr + r_buf->offset, r_buf->size - r_buf->offset); if (nbytes == -1) { printf("read err=%d\n", errno); } r_buf->offset += nbytes; printf("buffer: read=%.*s\n", r_buf->offset, r_buf->ptr); printf("event: ident=%lu, filter=%d, flags=%u, fflags=%u, data=%ld, udata=%p\n", event.ident, event.filter, event.flags, event.fflags, event.data, event.udata); //* //event.filter = EVFILT_WRITE; //event.flags = EV_ADD | EV_ONESHOT | EV_ENABLE; event.ident = e->ident; event.filter = EVFILT_WRITE; event.flags = EV_ADD | EV_ONESHOT | EV_ENABLE; event.fflags = 0; event.data = 0; event.udata = r_buf; if (kevent(kq_fd, &event, 1, NULL, 0, NULL) == -1) { printf("kevent err=%d, while register events.\n", errno); return -1; } //*/ } } } else if (e->filter == EVFILT_WRITE) { // write write(e->ident, "Ok\n", 3); printf("event: ident=%lu, filter=%d, flags=%u, fflags=%u, data=%ld, udata=%p\n", event.ident, event.filter, event.flags, event.fflags, event.data, event.udata); //* //event.ident = e->ident; //event.filter = EVFILT_READ; //event.flags = EV_ADD | EV_ENABLE; event.ident = e->ident; event.filter = EVFILT_READ; event.flags = EV_ADD | EV_ENABLE; event.fflags = 0; event.data = 0; event.udata = r_buf; if (kevent(kq_fd, &event, 1, NULL, 0, NULL) == -1) { printf("kevent err=%d, while register events.\n", errno); return -1; } //*/ } } } return 0; }
相关推荐
IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程。IO多路复用适用如下场合: (1)当客户处理多个描述字时(一般是交互式输入和网络套接口),必须使用I/O复用。 (2)当一个客户...
首先,"多路复用"是指在单个引脚上控制多个设备的能力,这是通过GPIO端口的多路复用功能实现的。STM32芯片具有强大的GPIO复用功能,允许一个引脚在不同功能之间切换,例如,既可以作为普通输入/输出,也可以作为I²C...
在Windows操作系统中,多路复用I/O(Multiplexed Input/Output)是一种高效地管理多个网络连接的技术,它允许程序同时处理多个套接字事件,而无需为每个连接创建单独的线程或进程。本篇文章将深入探讨三种在Windows...
在HMI设计中,多路复用功能是一种高效的方法,能够大大提升编程效率,特别是在处理大量相似数据或变量时。 在PLC编程中,当需要控制多个相似设备,如伺服轴时,通常会使用用户定义的数据类型(UDT)和数组。UDT允许...
本示例主要讲解如何使用TIA博途Wincc中的多路复用变量功能来实现多台相同设备参数的画面精简,从而提高项目的效率和操作便利性。 首先,多路复用变量是解决同一类型设备数量众多时,减少画面重复的一种技术。在本...
配置多路复用线路有许多种不同方法,多路复用器的类型也各异,常用的有频分多路复用(FDM)、时分多路复用(TDM)、波分多路复用(WDM)、码分多路复用(CDM)等。 频分多路复用(FDM)是一种常用的多路复用技术,...
Socket多路复用是一种在计算机网络编程中广泛使用的机制,主要应用于服务器端,它允许多个客户端连接同时处理,而无需为每个连接创建新的独立线程或进程。这种技术极大地提高了服务器的并发处理能力,降低了资源消耗...
Nginx以其卓越的性能和稳定性在Web服务器市场占据一席之地,其核心特性之一就是其对多路复用并发的支持。 **多进程模型** 在多进程模型中,服务器启动时会创建多个子进程来处理客户端请求。每个子进程都是独立的,...
在IT领域,服务器是网络应用的核心,而"基于多路复用的阻塞服务器"是一种高效处理并发请求的技术。多路复用(Multiplexing)技术允许服务器通过单个套接字同时处理多个连接,显著提高了系统资源的利用率,降低了系统...
IO 多路复用解析 IO 多路复用是一种高效的输入/输出(I/O)管理机制,允许单个进程同时监控多个文件描述符(FD),以便在其中某个文件描述符上发生事件时,立即响应。IO 多路复用机制广泛应用于服务器端编程、网络...
基于cyclone2 (EP2C8Q)设计的多路复用器(4通道8位带三态输出)Verilog源码 quartus 9.0工程 module MUX_4_8 ( //Input ports. DI_SEL, DI_EN, DI_0, DI_1, DI_2, DI_3, //Output ports. DO ); //======...
在IT领域,IO多路复用是一种高效地管理多个并发连接的技术,尤其在服务器端编程中,它能显著提升系统资源利用率和响应速度。本文将深入探讨如何利用IO多路复用来实现一个TCP echo服务器,该服务器运行在Linux环境下...
《gsm0710muxd多路复用源码及协议文档》是关于GSM07.10多路复用协议的重要参考资料,对于理解并应用这一通信协议有着极其关键的作用。GSM07.10是全球移动通信系统(Global System for Mobile Communications, GSM)...
西门子之地址多路复用如何工作?zip,西门子之地址多路复用如何工作?在地址多路复用中,根据多路复用变量的数值修改变量的地址参数。 地址多路复用的目的: 通过地址复用,可使用一个变量来寻址控制器地址区中的多个...
串口多路复用协议(Serial Multiplexing Protocol),通常被称为GSM07.10或CMUX(Control Multiplexer),是一种在串行通信中实现多路复用的技术,广泛应用于GSM网络中的BSS(Base Station Subsystem,基站子系统)...
多路复用是一种在通信系统中整合多个独立信号的技术,使得多个数据流可以在同一信道或媒介上传输,从而提高信道的利用率和系统的效率。本篇主要介绍节目复用、系统复用以及数据增值业务,同时涉及MPEG-2编码中的PES...
多路复用IO是一种高效的I/O管理策略,允许一个单独的线程或者进程同时处理多个I/O事件,极大地提高了系统的并发能力。在本主题中,我们将探讨如何实现一个多路复用IO的简单系统,以实现同时处理标准输入和TCP连接...
在Windows环境下进行网络编程,Socket多路复用是一种高效处理多个连接请求的技术,它允许多个套接字(sockets)在同一时刻被监控,以便服务端能够同时处理多个客户端的连接。这种技术通常通过`select`函数来实现,...
在IT领域,多路复用技术是用于高并发网络编程的一种高效策略,它允许一个单一的线程同时处理多个连接。本项目通过`select`和`epoll`两种方法实现了这一功能,并使用`jmeter`进行了性能测试。下面将详细讨论这两个多...