IO从概念上来说,总共有5种:
(1)阻塞IO (blocking I/O)
(2)非阻塞IO (nonblocking I/O)
(3)IO多路复用 (I/O multiplexing (select and poll))
(4)事件驱动IO (signal driven I/O (SIGIO))(Unix)
(5)异步IO (asynchronous I/O (the POSIX aio_functions))
不管文件IO还是网络socket的IO,其读写都需要经过两个阶段:
(1) wait for data(准备数据到内核的缓冲区)
(2) copy data from kernel to user (从内核缓冲区拷贝到用户空间)
在 linux 中,默认情况下所有的 socket 都是 blocking。
blocking IO 的特点就是在 IO 执行的两个阶段(等待数据和拷贝数据两个阶段)都被 block 了。
Linux 下,可以通过设置 socket 使其变为 non-blocking。当对一个 non-blocking socket 执行读操作时,流程是这个样子
从图中可以看出,当用户进程发出 read 操作时,如果 kernel 中的数据还没有准备好,那 么它并不会 block 用户进程,而是立刻返回一个 error。
从用户进程角度讲 ,它发起一个 read 操作后,并不需要等待,而是马上就得到了一个结果。
使用非阻塞的接收方式,服务器线程可以通过循环调用 recv()接口,可以在单个线程内实现对所有连接的数据接收工作。但是上述模型绝不被推荐。因为循环调用 recv()将大幅度推高 CPU 占用率 ;此外,在这个方案中 recv()更多的是起到检测“操作是否完成”的作用,实际上操作系统提供了更为高效的检测“操作是否完成“作用的接口,例如 select()多路复用模式, 可以一次检测多个连接是否活跃。