在看UNIX环境下的高级编程时,发现只写了select和poll,对epoll的回调机制还是不太了解。IO多路复用首先要了解LINUX网络的IO多路复用。IO多路复用在Linux下包括三种,select、poll、epoll。从抽象的角度来看,它们的功能是相似的,但具体的细节是不同的。不同的是:首先会为相关事件注册一组文件描述符,然后阻塞等待某些事件发生或者等待超时。有关详细信息,请参阅下面的“如何使用”。IO多路复用可以关注多个文件描述符,但是对于这三种机制,不同数量级的文件描述符对性能的影响是不同的,下面会详细介绍。selectselect将监听的文件描述符分为三组,每组监听不同的需要执行的IO操作。readfds是需要读取的文件描述符,writefds是需要写入的文件描述符,exceptfds是需要处理异常事件的文件描述符。这三个参数可以使用NULL表示对应的事件不需要监听。当select返回时,每组文件描述符都会被select过滤,只留下能进行相应IO操作的文件描述符。linux下的fd_set是一个1024位的位图,每一位代表一个fd的值,返回后需要扫描位图,这也是效率低下的原因。撇开性能问题不谈,正确性问题更值得关注。因为这是一个1024位的位图,当进程中的fd值>=1024时,就会越界,可能会导致崩溃。对于服务器程序来说,fd>=1024很容易实现,只要连接数+打开文件数足够大就可以实现。我们来看看fd_set结构是如何记录一批文件描述符是否被事件触发的。仔细看fd_set结构体的定义,可以发现它其实是一个__int32_t类型的数组,数组的所有元素加起来为1024位(由FD_SETSIZE定义)。在记录文件描述符是否触发事件时,一个bit代表文件描述符的状态,0表示没有触发事件,1表示触发,将文件描述符的值映射到一个下标,计算出哪个bit代表文件的状态的描述符。/*其实就是创建了一个longint位图数组,数组的大小为__FD_SETSIZE/__NFDBITS=64;所以它只能处理fd值小于1024的描述符,否则数组越界,其行为未定义*/所以可以看出,一旦文件描述符的值超过1024,计算出的下标可能会超出__int32_t数组的最大下标位置,所以可能会出现数组越界的问题。poll和select使用三组文件描述符的区别在于poll只有一个pollfd数组,数组中的每个元素代表一个需要监听IO操作事件的文件描述符。events参数是我们需要关心的事件,revents是所有内核监听的事件。Poll机制突破了Select机制中最大1024个文件描述符的限制。epollepoll_create用于创建epoll实例,epoll_ctl用于在epoll实例中添加、删除、修改需要监听的文件描述符,epoll_wait用于阻塞等待可以进行IO操作的文件描述符,直到超时。添加到文件描述符中的内容实际上会与网卡建立回调机制,即当真正发生时,会自主调用一个回调方法,并将事件所在的文件描述符插入到就绪队列中.参考程序可以直接从就绪队列中调用epoll_wait获取所有就绪的文件描述符,可以说时间复杂度是O(1)关于上面的回调机制如何实现,我还是参考level-triggered和epoll相关源码中的edge-triggeredlevel触发:只要socket可读/可写epollwait就会返回描述符。即只要socket的接收缓冲区有数据或者发送缓冲区有空间容纳要发送的数据。该套接字将由epoll_wait返回。边缘触发:当套接字的缓冲状态改变时返回。对于读取缓冲,当新到达的数据添加到读取缓冲区时触发。对于writebuffer,当buffercapacity变化时触发(peer确认数据包,kernel删除确认数据包,释放空间,writebuffercapacity变化)。不同IO多路复用方案的优缺点pollvsselectpoll和select基本相同,poll比select有以下优点:poll参数传递更人性化。比如不需要像select一样去计算很多奇怪的参数,比如nfds(最大值+1的文件描述符),传入的参数也不需要分开三组。poll的性能会比select稍微好一些,因为select会检测每一位。假设有一个值为1000的文件描述符,select会从第1位开始检测到第1000位。但是poll检测到一个数组。返回select的时间参数时,各个系统的处理方式不统一。如果希望程序更便携,每次调用select时都需要初始化time参数。select比poll好在以下几点:支持select的系统更多,兼容性更好。有些Unix系统不支持pollselect提供更高精度(微秒)的超时时间,而poll只提供毫秒级的精度。但是总的来说,select和poll基本是一样的。epollvspoll&selectepoll优于select&poll的有以下几点:当需要同时监听的文件描述符数量增加时,select&poll是O(N)复杂度,而epoll是O(1)。当N较小时,差距不大。会很大,但是如果N很大,一个O(N)的循环可以比O(1)慢很多,所以高性能的web服务器会选择epoll进行IO多路复用。epoll在内部使用文件描述符挂载需要监控的文件描述符。这个epoll文件描述符可以被多个线程/进程共享,所以epoll比select&poll有更多的使用场景。参考https://zhuanlan.zhihu.com/p/...https://www.jianshu.com/p/019...https://www.cnblogs.com/Anker...https:///www.cnblogs.com/anker...
