彻底理解 IO 多路复用实现机制( 六 )


支持一个进程所能打开的最大连接数

  • select:单个进程所能打开的最大连接数有FD_SETSIZE宏定义 , 其大小是32个整数的大小(在32位的机器上 , 大小就是32_32 , 同理64位机器上FD_SETSIZE为32_64) , 当然我们可以对进行修改 , 然后重新编译内核 , 但是性能可能会受到影响 , 这需要进一步的测试 。
  • poll:poll本质上和select没有区别 , 但是它没有最大连接数的限制 , 原因是它是基于链表来存储的 。
  • epoll:虽然连接数有上限 , 但是很大 , 1G内存的机器上可以打开10万左右的连接 , 2G内存的机器可以打开20万左右的连接 。
FD剧增后带来的IO效率问题
  • select:因为每次调用时都会对连接进行线性遍历 , 所以随着FD的增加会造成遍历速度慢的“线性下降性能问题” 。
  • poll:同上
  • epoll:因为epoll内核中实现是根据每个fd上的callback函数来实现的 , 只有活跃的socket才会主动调用callback , 所以在活跃socket较少的情况下 , 使用epoll没有前面两者的线性下降的性能问题 , 但是所有socket都很活跃的情况下 , 可能会有性能问题 。
消息传递方式
  • select:内核需要将消息传递到用户空间 , 都需要内核拷贝动作
  • poll:同上
  • epoll:epoll通过内核和用户空间共享一块内存来实现的 。
总结select , poll实现需要自己不断轮询所有fd集合 , 直到设备就绪 , 期间可能要睡眠和唤醒多次交替 。 而epoll其实也需要调用epoll_wait不断轮询就绪链表 , 期间也可能多次睡眠和唤醒交替 , 但是它是设备就绪时 , 调用回调函数 , 把就绪fd放入就绪链表中 , 并唤醒在epoll_wait中进入睡眠的进程 。 虽然都要睡眠和交替 , 但是select和poll在“醒着”的时候要遍历整个fd集合 , 而epoll在“醒着”的时候只要判断一下就绪链表是否为空就行了 , 这节省了大量的CPU时间 。 这就是回调机制带来的性能提升 。
select , poll每次调用都要把fd集合从用户态往内核态拷贝一次 , 并且要把current往设备等待队列中挂一次 , 而epoll只要一次拷贝 , 而且把current往等待队列上挂也只挂一次(在epoll_wait的开始 , 注意这里的等待队列并不是设备等待队列 , 只是一个epoll内部定义的等待队列) 。 这也能节省不少的开销 。
高频面试题什么是IO多路复用?看完上面的文章 , 相信你可以回答出来了 。
nginx/redis 所使用的IO模型是什么?Nginx的IO模型Nginx 支持多种并发模型 , 并发模型的具体实现根据系统平台而有所不同 。
在支持多种并发模型的平台上 , nginx 自动选择最高效的模型 。 但我们也可以使用 use 指令在配置文件中显式地定义某个并发模型 。
NGINX中支持的并发模型:
1、selectIO多路复用、标准并发模型 。 在编译 nginx 时 , 如果所使用的系统平台没有更高效的并发模型 , select 模块将被自动编译 。 configure 脚本的选项:–with-select_module 和 --without-select_module 可被用来强制性地开启或禁止 select 模块的编译
2、pollIO多路复用、标准并发模型 。 与 select 类似 , 在编译 nginx 时 , 如果所使用的系统平台没有更高效的并发模型 , poll 模块将被自动编译 。 configure 脚本的选项:–with-poll_module 和 --without-poll_module 可用于强制性地开启或禁止 poll 模块的编译
3、epollIO多路复用、高效并发模型 , 可在 Linux 2.6+ 及以上内核可以使用
4、kqueueIO多路复用、高效并发模型 , 可在 FreeBSD 4.1+, OpenBSD 2.9+, NetBSD 2.0, and Mac OS X 平台中使用
5、/dev/poll高效并发模型 , 可在 Solaris 7 11/99+, HP/UX 11.22+ (eventport), IRIX 6.5.15+, and Tru64 UNIX 5.1A+ 平台使用
6、eventport高效并发模型 , 可用于 Solaris 10 平台 , PS:由于一些已知的问题 , 建议 使用/dev/poll替代 。


推荐阅读