实现IO多路复用的方法主要是依赖系统提供的select
和poll方法
,在对它们进行描述之前,我们需要回过头看一下IO多路复用出现的大背景,先了解一下Unix提供给我们的五种I/O模型:
- blocking I/O
- nonblocking I/O
- I/O multiplexing(
select
andpoll
) - signal driven I/O(
SIGIO
) - asynchronous I/O(the POSIX
aio_
functions)
阻塞式IO
要理解IO多路复用,首先要了解普通的IO模型,通常的IO操作都是阻塞式IO,当对socket句柄调用recv时,如果没有收到数据,那么线程就会被挂起,收到数据时被唤醒继续执行。
如果server要处理N个Socket连接,那么就需要开N个线程来处理这N个连接,recv会使大部分线程进入阻塞状态,如果N很大,那么光是创建这些线程就会消耗大量的内存空间;
另外大量的线程间切换也会会导致每个线程能分到的CPU时间较少,使程序的性能变差。
所以阻塞式IO只适用并发量小的网络应用开发。
非阻塞式IO
非阻塞IO顾名思义就是调用IO方法不会阻塞线程,例如在调用recv的时候,如果内核缓冲区有数据就返回数据,没有数据就返回例如EWOULDBLOCK
的错误码。设置为非阻塞的方法也比较简单,可以通过fcntl(POSIX)或ioctl(Unix)设为句柄设置为非阻塞。虽然这样不会阻塞了,但是还是需要不断的对众多句柄调用recv方法进行轮训,这样会消耗大量的CPU资源。
非阻塞式IO适用并发量较小、且不需要及时响应的网络应用开发;
I/O多路复用
多路复用是使用一个线程来检查多个socket描述符的ready状态,比如调用select
函数,传入多个socket描述符,有任意一个socket描述符对应的IO在内核缓冲区有数据时就返回,否则会阻塞直到超时。对ready的socket描述符进行recv时,可以放到一个线程,也可以新开线程执行。
实现I/O多路复用的三种方案: select
、poll
、epoll
;
特点:
- 专一进程解决多个进程IO的阻塞问题,性能好;
- 实现、开发应用难度较大;
- 适用高并发服务应用开发:一个进程(线程)响应多个请求;
信号驱动IO模型

特点:回调机制,实现、开发应用难度大;
异步IO模型

几种IO模型的比较

- A synchronous I/O operation causes the requesting process to be blocked until that I/O operation completes.
- An asynchronous I/O operation does not cause the requesting process to be blocked.
Using these definitions, the first four I/O models—blocking, nonblocking, I/O multiplexing, and signal-driven I/O—are all synchronous because the actual I/O operation (recvfrom) blocks the process. Only the asynchronous I/O model matches the asynchronous I/O definition.