本文建立在读者已了解IO复用机制的select和epoll模式的基础上进行的总结,如无此方面基础可以翻阅我之前的博客:
特征 | select | epoll |
---|---|---|
内核实现 | 通过轮询方式检测就绪事件 | 通过回调方式检测就绪事件 |
时间复杂度 | O(n) | O(1) |
触发模式 | LT电平触发 | LT电平触发,ET边沿触发 |
内部实现 | select内部使用数组实现 | epoll维护的红黑树在共享内存中 |
内核区到用户区的转换 | 需要做内核区到用户区的转换,还需要做数据拷贝。 | epoll不需要做内核区到用户区的转换,因为数据存在共享内存中,使用了 mmap。内核区和用户区去操作共享内存,因此不需要区域转换,也不需要拷贝操作。 |
最大文件描述符个数 | 由宏FD_SETSIZE 设置,默认值是 1024,解决办法有两种,一是修改宏然后重新编译内核,但与此同时会引起网络效率的下降;二是使用多进程来解决,但是创建多个进程是有代价的,而且进程间数据同步没有多线程间方便。 | 1G内存是10万左右,以此类推 |
阻塞模式 | 每次调用select都进行“维护等待队列”和“阻塞进程”两个步骤。 | 将“维护等待队列”和“阻塞进程”两个步骤分开,先用epoll_ctl维护等待队列,再调用epoll_wait阻塞进程。 |
为什么规定select最大文件描述符数为1024?
因为每次调用select都需要将进程加入到所有监视socket的等待队列,每次唤醒都需要从每个队列中移除。这里涉及了两次遍历,而且每次都要将整个fds列表传递给内核,有一定的开销,进程被唤醒后,程序并不知道哪些socket收到数据,还需要遍历一次。正是因为遍历操作开销大,出于效率的考量,才会规定select的最大监视数量,默认只能监视1024个socket。