IO多路复用

问题:最传统的多进程并发模型,每进来一个新的I/O流会分配一个新的进程管理。

IO多路复用(I/O Multiplexing):单个进程,通过记录跟踪每个 IO 流的状态,来同时管理多个 IO 流。内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程。

与多进程和多线程技术相比,I/O多路复用技术的最大优势是系统开销小,系统不必创建进程/线程,也不必维护这些进程/线程,从而大大减小了系统的开销。

常见的I/O多路复用实现有系统调用 selectpollepoll,I/O多路复用就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。

但select,pselect,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。

初级 IO 复用

采用非阻塞的模式,当一个连接过来时,不阻塞住,这样一个进程可以同时处理多个连接了,然后进程就轮询这些连接,如果这些连接都没有I/O事件,就会造成CPU的空转,并且效率也很低。

select、poll

通过将用户的 IO 请求阻塞在selectpoll等系统调用上,即可实现同一线程/进程内同时处理多个 IO 请求。

当没有I/O事件的时候,这个进程处于阻塞状态;当有I/O事件的时候,selectpoll就唤醒进程去处理。

select、poll的原理是一样的,但是 select 观察的连接有限(32个整数的大小),poll可以观察无限个连接(基于链表来存储的)。

缺点:select、poll不知道哪个连接有 IO 事件需要处理,还是得去轮询连接。

epoll

epoll 的原理:当连接有I/O流事件产生的时候,epoll就会去告诉进程哪个连接有I/O流事件产生,然后进程就去处理这个进程。

epoll是线程安全的,而且只有 linux 支持。
epoll 连接数虽然有上限但是很大,1G 内存可以打开 10 万左右的连接。

epoll和select/poll区别是:

  • epoll内部使用了mmap共享了用户和内核的部分空间,避免了数据的来回拷贝。
  • epoll基于事件驱动,epoll_ctl注册事件并注册callback回调函数,epoll_wait只返回发生的事件避免了像select和poll对事件的整个轮寻操作。