IO层面的同步/异步与阻塞/非阻塞
🔄

IO层面的同步/异步与阻塞/非阻塞

Created
Aug 5, 2022 09:19 AM
Tags
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 (从内核缓冲区拷贝到用户空间)

阻塞 IO(blocking IO)

在 linux 中,默认情况下所有的 socket 都是 blocking。
notion image
 
blocking IO 的特点就是在 IO 执行的两个阶段(等待数据和拷贝数据两个阶段)都被 block 了。

非阻塞 IO(non-blocking IO)

Linux 下,可以通过设置 socket 使其变为 non-blocking。当对一个 non-blocking socket 执行读操作时,流程是这个样子
notion image
 
从图中可以看出,当用户进程发出 read 操作时,如果 kernel 中的数据还没有准备好,那 么它并不会 block 用户进程,而是立刻返回一个 error。
从用户进程角度讲 ,它发起一个 read 操作后,并不需要等待,而是马上就得到了一个结果。
使用非阻塞的接收方式,服务器线程可以通过循环调用 recv()接口,可以在单个线程内实现对所有连接的数据接收工作。但是上述模型绝不被推荐。因为循环调用 recv()将大幅度推高 CPU 占用率 ;此外,在这个方案中 recv()更多的是起到检测“操作是否完成”的作用,实际上操作系统提供了更为高效的检测“操作是否完成“作用的接口,例如 select()多路复用模式, 可以一次检测多个连接是否活跃。

IO多路复用(IO multiplexing)

IO multiplexing 即多路复用IO,select/poll/epoll就是属于此种类型。有些地方 也称这种 IO 方式为事件驱动 IO(event driven IO)(Unix系统下)
poll和select在本质上没有多大差别,但是poll没有最大文件描述符数量的限制。
epoll 通过两个方面,很好解决了 select/poll 的问题。 第一点,epoll 在内核里使用红黑树来跟踪进程所有待检测的文件描述字,把需要监控的 socket 通过 epoll_ctl() 函数加入内核中的红黑树里,红黑树是个高效的数据结构,增删查一般时间复杂度是 O(logn),通过对这棵黑红树进行操作,这样就不需要像 select/poll 每次操作时都传入整个 socket 集合,只需要传入一个待检测的 socket,减少了内核和用户空间大量的数据拷贝和内存分配。 第二点, epoll 使用事件驱动的机制,内核里维护了一个链表来记录就绪事件,当某个 socket 有事件发生时,通过回调函数内核会将其加入到这个就绪事件列表中,当用户调用 epoll_wait() 函数时,只会返回有事件发生的文件描述符的个数,不需要像 select/poll 那样轮询扫描整个 socket 集合,大大提高了检测的效率。
select/epoll 这个 function会不断的轮询所负责的所有socket,当某个 socket有数据到达了,就通知用户进程。
notion image
 
当用户进程调用了select,那么整个进程会被 block,而同时kernel会“监视”所有select负责的socket,当任何一个 socket 中的数据准备好了,select 就会返回
这个时候用户进程再调用 read 操作,将数据从 kernel 拷贝到用户进程
使用 select 以后最大的优势是用户可以在一个线程内同时处理多个socket的 IO请求。

异步 IO(Asynchronous I/O)

Linux 内核从 2.6 开始,也引入了支持异步响应的 IO 操作,如 aio_read, aio_write,这就是异步 IO。
notion image
 
用户进程发起 read 操作之后,立刻就可以开始去做其它的事。而另一方面,从 kernel 的角度,当它受到一个asynchronous read之后,首先它会立刻返回,所以不会对用户进程产生任何 block。然后,kernel 会等待数据准备完成,然后将数据拷贝到用户内存,当 这一切都完成之后,kernel 会给用户进程发送一个 signal,告诉它 read 操作完成了。
 

知乎陈硕的回答

在处理 IO 的时候,阻塞和非阻塞都是同步 IO。
只有使用了特殊的 API 才是异步 IO。
notion image
 
参考文章
网络基础之五种常见IO模型 https://zhuanlan.zhihu.com/p/364358152 各种IO复用模式之select,poll,epoll,kqueue,iocp分析 https://cloud.tencent.com/developer/article/1373483 select,poll,epoll的区别以及使用方法 https://blog.csdn.net/qq_37869098/article/details/122519170 一文搞懂用户缓冲区与内核缓冲区 https://blog.csdn.net/Jiangtagong/article/details/108703123 JAVA中的BIO/NIO/AIO与Selector https://blog.csdn.net/weixin_44273302/article/details/115269745