导航
导航
文章目录
  1. 阻塞式 I/O
  2. 非阻塞 I/O
  3. I/O 复用
  4. 信号驱动式 I/O
  5. 异步 I/O
  6. 各种 I/O 模型的比较
  7. 同步 I/O 和 异步 I/O

Unix 网络 I/O 模型

这篇文章主要是对《Unix 网络编程卷1》第六章第二小节的一个总结。

Unix 的网络 IO 模型主要有:

  • 阻塞式 I/O
  • 非阻塞式 I/O
  • I/O 复用
  • 信号驱动式 I/O
  • 异步 I/O

在介绍各种模型之前,先明确一个知识点:TCP 在建立连接之后,操作系统内核会为每一个连接创建相应的资源,比如接收缓冲区,发送缓冲区。TCP 的全双工模式,滑动窗口,重传等功能都是在两端的各自缓冲区中进行。当我们的应用程序调用 send 函数时候,实际所做的事情就是把应用程序中的数据拷贝到操作系统内核的发送缓冲区中,仅此而已。至于数据什么时候发送给对端的连接,这个是由发送缓冲区决定的。同样的,我们的应用程序调用 read 函数,只是把接收缓冲区的数据拷贝到应用程序当中

UDP 的连接都有一个接收缓冲区,没有发送缓冲区,因为不需要判断对方是否正确接收等,所以不需要发送缓冲区,只要有数据就发。

用 read 操作举例,一个 read 操作通常包括:

  1. 等待接收缓冲区的数据准备好;
  2. 将数据从接收缓冲区拷贝到应用程序当中。

下面介绍网络 I/O 模型用 UDP 的 recvfrom 函数来做例子。之所以用 UDP 的 recvfrom 是因为对于 UDP 而言,操作 1 中的数据准备好这个概念比较简单:要么整个数据已经收到,要么还没有。对于 TCP 而言这个概念相对来说比较复杂。这里可以将 recvfrom 函数看作系统调用,用来区分应用程序和内核。

阻塞式 I/O

阻塞式 I/O 模型

如上图所示,应用程序调用 recvfrom 函数,直到数据报到达且被复制到应用进程的缓冲区中或者发生错误才返回。从调用开始到返回数据整段时间内都是被阻塞的。recvfrom 函数成功返回后,才开始处理数据报。

非阻塞 I/O

非阻塞 I/O 模型

通过套接字设置,我们可以把应用程序的套接字设置成非阻塞的,应用程序调用 recvfrom 函数,当内核缓冲区没有数据时,应用程序的进程并不会被投入睡眠,而是返回一个 EWOULDBLOCK 错误。应用程序可以捕获处理这个错误,然后轮询调用 recvfrom 函数,直到内核缓冲区的数据准备好,数据被复制到应用程序。recvfrom 函数成功返回,接着处理数据。

I/O 复用

I/O 复用又被称为事件驱动。可以理解为这是内核提供的一个系统调用,如 select, poll, epoll 和 kequeue。向事件驱动注册相应的套接字事件,如读事件,写事件,事件驱动用来监视这些套接字的事件状态。当某些套接字就绪(可读或者可写)时,事件驱动会返回相应的套接字描述符。

I/O 复用模型

如上图所示,当应用程序调用 select 函数时,程序阻塞在这个系统调用上,而不是阻塞在真正的 I/O 系统调用上。当 select 返回的套接字可读时,我们调用 recvfrom 把所读的数据报复制到应用程序中。使用 I/O 复用的优势在于我们可以等待多个描述符就绪。

信号驱动式 I/O

信号驱动式 I/O 模型

信号驱动 I/O 是让内核在套接字描述符就绪时通过发送 SIGIO 信号来通知应用程序。
如上图,首先开启套接字的信号驱动式 I/O 功能,并通过 sigaction 系统调用定义一个信号处理函数。该系统调用将立即返回,应用程序继续工作,也就是说它没有阻塞。当数据报准备好读取时,内核就为该应用程序发送一个 SIGIO 信号。我们可以在之前定义的信号处理函数中调用 recvfrom 读取数据报,并通知主循环数据已准备好待处理,也可以立即通知主循环,让它读取数据报。

这种模型的优势在于等待数据准备好期间应用程序不阻塞。主循环可以继续执行,只要等待来自信号处理函数的通知即可。

异步 I/O

异步 I/O 模型

异步 I/O 模型是告知内核启动某个动作(读,写等),并让内核在整个操作(包括将数据从内核复制到应用程序的缓冲区)完成后通知我们。这种模型和信号驱动式 I/O 模型的区别在于:信号驱动式 I/O 是由内核通知应用程序何时可以启动一个 I/O 操作,而异步 I/O 模型是 I/O 操作也由此模型完成,等I/O 操作完成之后内核再通知应用程序。

如上图,调用 aio_read, 该系统调用立即返回,而且等待 I/O 完成期间,应用程序是不被阻塞的。

各种 I/O 模型的比较

5 种 I/O 模型的比较

上图可以看出,前 4 种模型的主要区别在于第一阶段,因为它们的第二阶段是一样的:在数据从内核复制到应用程序期间,进程阻塞于 recvfrom 调用。相反,异步 I/O 模型在这两个阶段都要处理。

同步 I/O 和 异步 I/O

POSIX 把这两个术语定义如下:

  • 同步 I/O: I/O 操作导致请求进程阻塞,直到 I/O 操作完成。
  • 异步 I/O: I/O 操作不导致请求进程阻塞。

根据上述同步 I/O 和异步 I/O 的定义,我们很容易的能够得出:阻塞 I/O, 非阻塞 I/O, I/O 复用,信号驱动式 I/O 都是同步 IO 模型。只有 异步 I/O 是 POSIX 定义的异步 I/O.

支持一下
请 xdd1874 喝一杯咖啡?
  • 微信扫一扫