五种 I/O 模型:
- blocking I/O 阻塞 I/O
- nonblocking I/O 非阻塞 I/O
- I/O multiplexing (
select
andpoll
) I/O 多路复用(select
和poll
) - signal-driven I/O (
SIGIO
) 信号驱动 I/O (SIGIO
) - asynchronous I/O (the POSIX
aio_
functions) 异步 I/O(POSIXaio_
函数)
输入操作通常有两个不同的阶段:
1.等待数据准备就绪。这涉及等待数据到达网络。当数据包到达时,它被复制到内核内的缓冲区中。
2.将数据从内核复制到进程。这意味着将(就绪的)数据从内核的缓冲区复制到我们的应用程序缓冲区中
blocking I/O
在上图中,进程调用 recvfrom
和系统调用不会返回,直到数据报到达并被复制到我们的应用程序缓冲区中,或者发生错误。最常见的错误是系统调用被信号打断,从调用 recvfrom
到返回,进程一直被阻止。成功返回后 recvfrom
,我们的应用程序将处理数据报。
nonblocking I/O
当套接字设置为非阻塞时,我们告诉内核“当我请求的 I/O 操作在不让进程进入睡眠状态的情况下无法完成时,不要让进程进入睡眠状态,而是返回错误”。图如下图:
对于前三个 recvfrom
,没有要返回的数据,内核会立即返回错误 EWOULDBLOCK
当我们第四次调用 recvfrom 时,数据报已准备就绪,它被复制到我们的应用程序缓冲区中,并 recvfrom
成功返回。然后,我们处理数据。
当应用程序处于调用 recvfrom
非阻塞描述符的循环中时,它称为轮询。应用程序会不断轮询内核,以查看某些操作是否已准备就绪。这通常是浪费 CPU 时间,但偶尔会遇到这种模型,通常在专用于一个功能的系统上。
I/O multiplexing
使用 I/O 多路复用时,我们在这两个系统调用之一中调用 select
or poll
和 block,而不是在实际的 I/O 系统调用中进行阻塞。下图是 I/O 多路复用模型的摘要
我们阻止对 select
的调用,等待数据报套接字可读。当 select
返回套接字是可读的时,我们调用 recvfrom
将数据报复制到我们的应用程序缓冲区中。
与阻塞 I/O 模型的比较
缺点:使用 select
需要两个系统调用( select
和 recvfrom
)而不是一个
优点:我们可以等待多个描述符(数据)准备就绪
signal-driven I/O
信号驱动的 I/O 模型使用信号,告诉内核在描述符准备就绪时用 SIGIO
信号通知我们。图如下图:
我们首先启用信号驱动 I/O 的套接字,并使用 sigaction
系统调用安装信号处理程序。这个系统调用的回报是立即的,我们的过程仍在继续;它没有被阻止。
当数据报准备好读取时,就会为我们的过程生成 SIGIO
信号。我们可以:
1.通过调用 recvfrom
从信号处理程序读取数据报,然后通知主循环数据已准备好处理
2.通知主循环并让它读取数据报
这个模型的优点是,在等待数据报到达时,我们不会被阻塞。主循环可以继续执行,只需等待信号处理程序通知数据已准备好处理或数据报已准备好读取。
asynchronous I/O
异步 I/O 由 POSIX 规范定义,并且已经协调了各种标准中出现的实时函数的各种差异,这些差异共同构成了当前的 POSIX 规范。
这些函数的工作原理是告诉内核启动操作,并在整个操作(包括将数据从内核复制到缓冲区)完成时通知我们。此模型与信号驱动 I/O 模型之间的主要区别在于,对于信号驱动 I/O,内核会告诉我们何时可以启动 I/O 操作,但对于异步 I/O,内核会告诉我们 I/O 操作何时完成。例如,请参见下图:
我们调用 aio_read
(POSIX 异步 I/O 函数以 aio_
or lio_
开头)并向内核传递以下内容:
1.描述符、缓冲区指针、缓冲区大小(相同的三个参数)、 read
2.文件偏移量(类似于 lseek
)
3.以及如何在整个操作完成后通知我们。
此系统调用会立即返回,并且在等待 I/O 完成时,我们的进程不会被阻止。
在这个例子中,我们假设我们要求内核在操作完成时生成一些信号。在将数据复制到我们的应用程序缓冲区之前,不会生成此信号,这与信号驱动的 I/O 模型不同。
I/O 模型比较
下图是五种不同 I/O 模型的比较。
前四个模型之间的主要区别在于第一阶段,因为前四个模型中的第二阶段是相同的:当数据从内核复制到调用方的缓冲区时,进程在调用中被 recvfrom
阻止。但是,异步 I/O 处理这两个阶段,并且与前四个阶段不同
Synchronous I/O 与 Asynchronous I/O
同步I/O 和 异步I/O的如下定义:
同步 I/O 操作会导致请求进程被阻止,直到 I/O 操作完成。
异步 I/O 操作不会导致请求进程被阻止。
使用以上定义,前四个 I/O 模型(阻塞、非阻塞、I/O 多路复用和信号驱动 I/O)都是同步的,因为实际的 I/O 操作 ( recvfrom
) 会阻塞该过程。只有异步 I/O 模型与异步 I/O 定义匹配。
参考:https://litux.nl/mirror/unixnetworkprogramming/0131411551_ch06lev1sec2.html
Chapter 6. I/O Multiplexing: The select and poll Functions - Shichao's Notes