select、poll、epoll浅析
这篇文章围绕 Linux I/O 多路复用,系统对比了 select、poll、epoll 的机制、使用方式与性能差异。三种模型的核心定位select/poll 都是“就绪检测器”,负责发现可读写事件而非直接做 I/O。两者都采用“每次调用都扫描 + 结果回传”的模式,本质都偏线性轮询。epoll 通过兴趣集合与就绪队…
整篇博客的完整示例代码在:githubselect介绍与使用 一、介绍: select系统调用的目的是:在一段指定时间内,监听用户感兴趣的文件描述符上的可读、可写和异常事件。poll和select应该被归类为这样的系统 调用,它们可以阻塞地同时探测一组支持非阻塞的IO设备,直至某一个设备触发了事件或者超过了指定的等待时间——也就是说它们的职责不是做IO,而是帮助 调用者寻找当前就绪的设备。 原理图:flowchart TB subgraph USER[用户空间] S1[应用准备监听集合] S2[调用 select 等待事件] S3[返回后检查就绪描述符] S4[对就绪描述符执行读写] end subgraph KERNEL[内核空间] SK1[内核复制监听集合] SK2[按描述符顺序线性扫描] SK3[检查可读 可写 异常状态] SK4[无就绪时挂起进程] SK5[事件到来后唤醒进程] SK6[回写就绪集合到用户空间] end S1 --> S2 --> SK1 --> SK2 --> SK3 SK3 --> SK6 --> S3 --> S4 SK3 --> SK4 --> SK5 --> SK2 二、使用 需要的系统调用API如下:#include <sys/time.h> #include <sys/types.h> #include <unistd.h> int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); 一般经过以下三个过程:定义bitmap结构,就是定义fd_set类型变量,内部实现是一个int数组,整个数组的bit位长 FD_SETSIZE,这个宏默认为1024。根据文件描述符大小设置bitmap,该步骤调用FD_SET宏进行设置。调用select函数,并传入bitmap,select内部会根据bitmap上的标记进行轮询,一旦有文件描述符触发事件就将其重新标记到用户态的bitmap里,select除了第一个参数外(第一个参数传入fd的最大值+1),后面连续三个传入的指针参数分别代表:监听读取事件的bitmap、监听写入事件的bitmap、监听异常事件的bitmap,最后一个参数表示延迟时间。根据bitmap得到已经准备好的文件描述符,并对其执行相应的操作。(Reactor模型 一个文件描述符是否准备就绪,有以下判断: 在网络编程中,下列情况下socket可读: a) socket内核接收缓冲区的字节数大于或等于其低水位标记SO_RCVLOWAT; b) socket通信的对方关闭连接,此时该socket可读,但是一旦读该socket,会立即返回0(可以用这个方法判断client端是否断开连接); c) 监听socket上有新的连接请求; d) socket上有未处理的错误。下列情况下socket可写: a) socket内核发送缓冲区的可用字节数大于或等于其低水位标记SO_SNDLOWAT; b) socket的读端关闭,此时该socket可写,一旦对该socket进行操作,该进程会收到SIGPIPE信号; c) socket使用connect连接成功之后; d) socket上有未处理的错误。 关键代码如下: //1.定义bitmap结构。 fd_set,是一个bitmap,大小为1024位,是一个长度为1024/32的int数组 fd_set rset; char buffer[MAXBUF]; while (1) { FD_ZERO(&rset); //重置为0 for (int i = 0; i < 5; ++i) { FD_SET(fds[i],&rset); //2.根据文件描述符标记bitmap } puts("round again\n"); select(max+1,&rset,NULL,NULL,NULL); //3.调用select进行轮询 for(auto &fd:fds){ if(FD_ISSET(fd,&rset)){ //4.获取已经准备好的描述符进行相应操作 memset(buffer, 0, MAXBUF); read(fd, buffer, MAXBUF); puts(buffer); } } } 优缺点 从以下三个角度来评析:支持的最大连接数:与 FD_SETSIZE 宏的大小有关,一般为1024。内核态到用户态的拷贝消耗:非常高,每次select调用都会重新copy一次。内核态扫描的数据结构:线性扫描,FD剧增后会造成很大的效率问题 我们发现上面所说的都是它的各方面特性,同时也体现出了它的缺点。 缺点:支持的最大文件描述符不…
正在初始化 WebAssembly 引擎…
首次编译原生模块可能需要数秒
就绪后,页面交互将以接近原生的速度运行