1 定义

I/O 多路复用的本质,是通过一种机制(系统内核缓冲I/O数据),让单个进程可以监视多个文件描述符,一旦某个描述符就绪(一般是读就绪或写就绪),能够通知程序进行相应的读写操作。

1.1 什么是文件描述符

文件描述仅仅就是一个数字而已,但是通过这个数字我们可以操作一个打开的文件,有了文件描述符,进程可以对文件一无所知,比如文件在磁盘的什么位置、加载到内存中又是怎样管理的等等,这些信息统统交由操作系统打理,进程无需关心,操作系统只需要给进程一个文件描述符就足够了。

image.png

2 Select

在select这种I/O多路复用机制下,我们需要把想监控的文件描述集合通过函数参数的形式告诉select,然后select会将这些文件描述符集合拷贝到内核中。

我们知道数据拷贝是有性能损耗的,因此为了减少这种数据拷贝带来的性能损耗,Linux内核对集合的大小做了限制,并规定用户监控的文件描述集合不能超过1024个,同时当select返回后我们仅仅能知道有些文件描述符可以读写了,但是我们不知道是哪一个。因此程序员必须再遍历一边找到具体是哪个文件描述符可以读写了。

总结下来select有这样几个特点:

<aside> 💡

因此我们可以看到,select机制的这些特性在高并发网络服务器动辄几万几十万并发链接的场景下无疑是低效的。

3 Poll

poll和select是非常相似的。

poll相对于select的优化仅仅在于解决了文件描述符不能超过1024个的限制,select和poll都会随着监控的文件描述数量增加而性能下降,因此不适合高并发场景。

4 Epoll

文件描述符集合的变化频率比较低,select和poll频繁的拷贝整个集合,内核都快被烦死了,epoll通过引入epoll_ctl很体贴的做到了只操作那些有变化的文件描述符。同时epoll和内核共享了同一块内存,这块内存中保存的就是那些已经可读或者可写的的文件描述符集合,这样就减少了内核和程序的拷贝开销。