阻塞与⾮阻塞 IO VS 同步与异步 IO

5 分钟阅读 664 字 + 609 词
参考答案
先来看看阻塞 I/O,当⽤户程序执⾏ read ,线程会被阻塞,⼀直等到内核数据准备好,并把数据从内核缓冲区拷⻉到应⽤程序的缓冲区中,当拷⻉过程完成, read 才会返回。 注意, 阻塞等待的是「内核数据准备好」和「数据从内核态拷⻉到⽤户态」这两个过程 。过程如下图:
image-20240920162026820
知道了阻塞 I/O ,来看看⾮阻塞 I/O,⾮阻塞的 read 请求在数据未准备好的情况下⽴即返回,可以继续往下执⾏,此时应⽤程序不断轮询内核,直到数据准备好,内核将数据拷⻉到应⽤程序缓冲区, read 调⽤才可以获取到结果。过程如下图:
image-20240920162052381
注意, ==这⾥最后⼀次 read 调⽤,获取数据的过程,是⼀个同步的过程,是需要等待的过程。这⾥的同步指的是内核态的数据拷⻉到⽤户程序的缓存区这个过程==
应⽤程序每次轮询内核的 I/O 是否准备好,感觉有点傻乎乎,因为轮询的过程中,应⽤程序啥也做不了,只是在循环。 I/O 多路复⽤技术就出来了,如 select、 poll,它是通过I/O 事件分发,当内核数据准备好时,再以事件通知应⽤程序进⾏操作
image-20240920162309618
实际上,== ⽆论是阻塞 I/O、⾮阻塞 I/O,还是基于⾮阻塞 I/O 的多路复⽤都是同步调⽤ 。因为它们在 read 调⽤时, 内核将数据从内核空间拷⻉到应⽤程序空间,过程都是需要等待的,也就是说这个过程是同步的,如果内核实现的拷⻉效率不⾼, read 调⽤就会在这个同步过程中等待⽐较⻓的时间。 ==
⽽真正的异步 I/O 是「内核数据准备好」和「数据从内核态拷⻉到⽤户态」这两个过程都不⽤等待。
当我们发起 aio_read 之后,就⽴即返回,内核⾃动将数据从内核空间拷⻉到应⽤程序空间,这个拷⻉过程同样是异步的,内核⾃动完成的,和前⾯的同步操作不⼀样,应⽤程序并不需要主动发起拷⻉动作。过程如下图:
image-20240920162427446
在前⾯我们知道了, I/O 是分为两个过程的
  1. 数据准备的过程
  2. 数据从内核空间拷⻉到⽤户进程缓冲区的过程
阻塞 I/O 会阻塞在「过程 1 」和「过程 2」,⽽⾮阻塞 I/O 和基于⾮阻塞 I/O 的多路复⽤只会阻塞在「过程 2」,所以这三个都可以认为是同步 I/O。 异步 I/O 则不同,「过程 1 」和「过程 2 」都不会阻塞。