之前程序中使用的IO函数都是同步的,无论阻塞式还是非阻塞式,在数据从内核拷贝到用户空间过程,用户线程都是被阻塞的。非阻塞IO只是当内核还没准备好数据时立即返回不等待,需要用户自己去不断检查内核数据是否准备好,依然不高效。IO多路复用提出了新的思路,将IO过程分为等待内核数据准备好和读取/写入内核两部分。一个IO函数监控多个IO可读/可写事件,任意1个IO设备准备好时返回(需要代码中轮询查看是哪个IO文件描述符,什么事件),再调用对应的read/write函数操作,减少不必要的等待时间,高效了很多。具体的实现有select、poll和epoll三种
select IO模型
select目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点。
它通过一个select()系统调用来监视多个文件描述符的数组,当select()返回后,该数组中就绪的文件描述符便会被内核修改标志位,使得进程可以获得这些文件描述符从而进行后续的读写操作
select对于单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024,不过可以通过修改宏定义甚至重新编译内核的方式提升这一限制。
select()所维护的存储大量文件描述符的数据结构,随着文件描述符数量的增大,其复制的开销也线性增长。同时,由于网络响应时间的延迟使得大量TCP连接处于非活跃状态,但调用select()会对所有socket进行一次线性扫描。这也浪费了很大有一部分开销。
poll IO模型
poll 与select 的本质是一样的,同样包含大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大,但是