跳至主要內容

阻塞非阻塞同步异步 IO

AruNi_Lu计算机基础操作系统约 1481 字大约 5 分钟

本文内容

1. IO 是什么?

IO 全称是 Input/Output,即输入输出,常见的 IO 有磁盘 IO,网路 IO 等。

当我们需要将内存中的数据持久化时,就需要向磁盘写数据,这个过程就是一次输出(从内存角度来说,将内存中的数据输出到磁盘,从磁盘角度来说则是输入了);反之,我们需要把数据从磁盘中取出时,就是一个输入的过程。这时磁盘就是一个 IO 设备,负责将数据具体的写入和读出。

当我们需要跨主机进行通信时,一个常用的方法就是使用网络通信,而网络通信过程中需要借助网络 IO 设备,比如网卡、路由器、交换机等,它们负责数据流的输入和输出。

对于我们经常使用的应用程序来说,在程序需要使用 IO 操作时,往往只是调用 API,比如:

  • 网络 IO:建立连接后,调用 send() 发送数据,recv() 接收数据;
  • 磁盘 IO:打开文件后,调用 write() 写数据,read() 读数据。

我们都知道,为了安全和稳定,操作系统将地址空间划分为用户空间和内核空间,只有 在内核空间才能访问 IO 设备

程序都是跑在用户空间的,所以程序调用的这些 API 也只是操作系统提供出来的系统调用函数,所以 只能通过操作系统提供的系统调用来间接的完成 IO 操作

正是因为用户和内核空间的划分,所以 程序一次 IO 调用,会经历两个过程

  • 内核收到程序的 IO 操作后访问 IO 设备,等待 IO 设备将数据准备好
  • 数据准备好后,内核需要将数据从内核空间拷贝到用户空间(一般读出来的数据会先放到缓冲区),此时程序才能读取到。

理解了 IO 和程序进行 IO 操作的流程,下面就来看看几种常见的 IO,它们分别是 阻塞 IO、非阻塞 IO、同步 IO 和异步 IO

2. 阻塞和非阻塞 IO

2.1 阻塞 IO

阻塞 IO 是指在发起 IO 调用后,调用方会一直阻塞等待,直到接收到数据 才进行后续的操作。

这个阻塞等待过程包括:用户发起 IO 调用 -> 内核准备好数据 -> 内核将数据拷贝到用户空间 -> 用户接收到数据

image-20230806221446109

2.2 非阻塞 IO

阻塞 IO 是发起系统调用后,一直阻塞直到数据返回,非阻塞 IO 与其相反,发起 IO 调用后,内核在未准备好数据时会立即返回,此时程序可以继续向下执行,只不过一般应用程序都会不断轮询内核,直到取到数据

不断轮询并不是一种阻塞,毕竟进程还是一直在跑的(一直循环调用)。

需要注意的是,当轮询到内核已准备好数据时,内核需要将数据拷贝到用户空间,这个过程调用方还是阻塞的

image-20230806225615909

注:EWOULDBLOCK 是一个与非阻塞 I/O 操作相关的错误码,表示在进行非阻塞操作时,暂时无法立即完成该操作,需要进一步等待才能继续进行

3. 同步和异步 IO

前面讲的 阻塞/非阻塞 IO 描述的是调用者发起调用后,是否持续等待直到数据返回。而 同步/异步 IO 描述的是调用者发起调用后,是否需要继续参与这个过程

其实前面的阻塞/非阻塞 IO,都是一种同步 IO,因为 调用者在发起调用后,还要继续参与这个过程

非阻塞 IO 在内核拷贝数据时也是阻塞等待的。

异步 IO 在发起调用后,内核会立即返回(表示调用成功,并不是返回结果),接着 只需要内核完成整个操作(包括准备数据、拷贝到用户空间),完成后通知应用进程即可,调用者是不需要参与整个过程的

image-20230806234858068

可以发现,调用者发起调用后,在内核准备数据和拷贝数据的过程中都是不需要参与和等待的

4. 总结

程序一次 IO 调用,会经历两个过程

  • 内核等待 IO 设备将数据准备好
  • 内核需要将数据从内核空间拷贝到用户空间

阻塞/非阻塞 IO:

  • 阻塞 IO 是发起系统调用后,一直阻塞直到数据返回
  • 非阻塞 IO 是发起 IO 调用后,内核在未准备好数据时会立即返回,此时程序可以继续向下执行,只不过一般应用程序都会不断轮询内核,直到取到数据

阻塞/非阻塞 IO 都是一种同步 IO,都有一段等待阻塞的过程,在此过程调用者是需要一直参与的。

同步/异步 IO:

  • 同步 IO 是 调用者在发起调用后,还要继续参与这个过程
  • 异步 IO 在发起调用后,调用者不需要参与整个过程,内核完成后通知应用进程即可
上次编辑于: