什么是阻塞,非阻塞,同步,异步?

2021.9更新
Todo:
5种IO模型、阻塞IO和非阻塞IO、同步IO和异步IO
Chapter6 of Unix Network Programming.vol3, 尤其是6.2 I/O model

========================
答案都来自知乎:

Answer1

所谓同步异步,只是对于水壶而言。 普通水壶,同步;响水壶,异步。 虽然都能干活,但响水壶可以在自己完工之后,提示老张水开了。这是普通水壶所不能及的。 同步只能让调用者去轮询自己(情况2中),造成老张效率的低下。

所谓阻塞非阻塞,仅仅对于老张而言。 立等的老张,阻塞;看电视的老张,非阻塞。 情况1和情况3中老张就是阻塞的,媳妇喊他都不知道。虽然3中响水壶是异步的,可对于立等的老张没有太大的意义。所以一般异步是配合非阻塞使用的,这样才能发挥异步的效用。

作者:知乎用户
链接:https://www.zhihu.com/question/26393784/answer/513257548

Answer2

作者:后端技术小黑板
链接:https://www.zhihu.com/question/26393784/answer/1480048712
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

在讨论问题之前,我们需要确定一下上下文,以免产生误解,本文讨论的背景是基于Linux的网络IO开展的实际上无论是哪一种IO模型,都可以分为两个阶段(以read操作为例):

  • 等待数据准备就绪
  • 将数据从内核态拷贝到用户空间

这里我们需要解释一下,什么是内核态和用户态

  • 用户态:也称为用户空间,即上层应用程序的活动空间,应用程序的执行必须依赖于内核提供的资
  • 内核态:也称为内核空间,控制计算机的硬件资源,并提供上层应用程序运行的环境

注:用户态可以通过系统调用切换到内核态,这是主动进入到内核态。此外在出现异常或者外围设备的中断也会进入到内核态,不过是被动响应的
先说结论:各种IO模型其实是根据它在上述两个阶段的不同表现(用户进程或者线程是否阻塞)来区分的!!!
在第一个阶段用户进程是否阻塞用来区分阻塞/非阻塞,在第二个阶段用户是否阻塞用来区分同步/异步!!!

阻塞IO

当用户进程发起系统调用recvfrom时,在第一阶段,内核首先会等待数据准备就绪,这个过程中用户进程是阻塞的;在第二阶段,当数据准备就绪之后,数据会从内核空间拷贝到用户空间(应用程序空间),这个过程中用户进程同样是阻塞的,直到数据拷贝完成才恢复。所以,阻塞IO在两个阶段都是阻塞的!!

非阻塞IO

非阻塞IO和阻塞IO相似,区别在于第一阶段,在等待数据准备就绪的过程中,用户进程不会阻塞,而是在数据没有准备就绪的时候得到一个error返回,这是用户进程就知道数据还没有准备好,然后就可以继续发起recvfrom操作直到数据准备就绪
在第二阶段,数据从内核空间向用户空间拷贝时,非阻塞IO用户进程同样是阻塞的

IO多路复用

IO多路复用实际上是通过select/epoll等函数轮询多个socket,当用户进程调用了select函数,整个用户进程就会被阻塞,直到收到数据准备就绪的返回后续的操作和非阻塞IO是类似的,由于在recvfrom系统调用之前,需要的数据就已经准备就绪了,所以第一个阶段是非阻塞的;第二个阶段同样还是阻塞的所以IO多路复用整个过程中,用户进程都是阻塞的~

异步IO

异步IO实际上是用户进程发起read操作之后,就会立刻收到一个返回,所以用户进程就可以去完成其他的工作,而不需要阻塞;直到数据准备就绪并且完成了从内核空间向用户空间拷贝的工作,这时用户进程会收到一个通知,告诉他read操作已完成整个过程中用户进程不会被阻塞

**总结

所以实际上阻塞IO和非阻塞IO都是同步IO,用到了特殊的异步函数才是异步IO如果能从IO过程的两个阶段来看待这些问题,就不会被这些概念和一些例子绕晕了**

Answer3

一个IO操作其实分成了两个步骤:发起IO请求和实际的IO操作。
阻塞IO和非阻塞IO的区别在于第一步,发起IO请求是否会被阻塞,如果阻塞直到完成那么就是传统的阻塞IO,如果不阻塞,那么就是非阻塞IO。
同步IO和异步IO的区别就在于第二个步骤是否阻塞:如果实际的IO读写阻塞请求进程,那么就是同步IO,因此阻塞IO、非阻塞IO、IO复用、信号驱动IO都是同步IO;如果不阻塞,而是操作系统帮你做完IO操作再将结果返回给你,那么就是异步IO。

  • 同步阻塞I/O:
    • 当进程调用某些设计I/O操作的系统调用或库函数时,比如accept()、send()、recv()等,进程便暂停下来,等待I/O操作完成后再继续运行。
  • 同步非阻塞I/O:(轮询)
    • 不会等待数据就绪,而是结合反复轮询来尝试数据是否就绪。
    • 与同步阻塞I/O相比,同步非阻塞I/O好处是在一个进程中可以同时处理多个I/O操作,而不是阻塞在一个I/O操作上
  • 多路I/O就绪通知:(I/O复用)
    • 允许进程通过一种方法来同时监听所有文件描述符,并可以快速获得所有就绪的文件描述符,然后只针对这些文件描述符进行数据访问。我们常用的select、poll、epoll等函数使用了I/O复用模型。
    • 与同步非阻塞I/O相比,I/O复用模型的优势在于可以同时等待多个(而不只是一个)套接字描述符就绪
  • 信号驱动式I/O:
    • 需要开启套接字的信号驱动I/O功能,并通过sigaction系统调用安装一个信号处理函数。sigaction函数立即返回,我们的进程继续工作,即进程没有被阻塞。当数据报准备好时,内核会为该进程产生一个SIGIO信号,这样我们可以在信号处理函数中调用recvfrom读取数据报,也可以在主循环中读取数据报。无论如何处理SIGIO信号,这种模型的优势在于等待数据报到达期间不被阻塞。
  • 异步I/O(AIO):
    • 启动某个操作,并让内核在整个操作(包括等待数据和将数据从内核复制到用户空间)完成后通知应用进程。
    • 与信号驱动式I/O的区别在于:信号驱动式I/O在数据报准备好时就通知应用进程,应用进程还需要将数据报从内核复制到用户进程缓冲区;而异步I/O模型则是整个操作完成才通知应用进程,应用进程在整个操作期间都不会被阻塞。