Socket 同步/异步 IO 以及阻塞/非阻塞的概念理解

阻塞/非阻塞 IO

阻塞:该函数在未得到预想结果,会阻塞进程,直到函数完成。

非阻塞:该函数在未得到预想结果,不会阻塞进程,而是会返回失败的错误,此时用户可以进行其他工作,之后用户可以多次调用该函数,直到成功。

一般意义上的同步/异步

  1. 同步:两个事件 A、B 必须以一定顺序执行,如 B 执行前,A 必须已执行,若 A 未执行完成,则 B 必须阻塞等待。

    Linux 通常使用 Mutex 完成这样的同步要求。

  2. 异步:两个事件没有固定的执行顺序要求。

Linux Socket IO 上的同步/异步

当同步/异步概念来到 Socket IO 时,需要注意它是针对内核 IO 操作的,发生在用户调用 IO 操作的系统调用时,不明白这一点,就可能会造成误解,误以为认为它是一个更为宏观的概念。

IO 操作是什么

数据:硬件->内核->用户态;

数据:用户态->内核->内核的过程。

如 socket 的读 IO 操作即是:

IO 操作即内核从 socket 读取数据,保存到内核缓冲区,然后将数据从内核缓冲区拷贝到用户态的缓冲区的过程。

理解同步/异步 IO

  1. 同步 IO

    开始 IO 操作后,整个 IO 过程中用户会一直阻塞,直到完成。

  2. 异步 IO

    开始 IO 操作后,用户可以从调用的函数中立即返回,等到数据拷贝到用户态缓冲区完成后,用户会收到通知的 signal。

由此看来,同步和异步的区别在于,实际的 IO 操作是怎么进行的,即是在是函数立即返回的非阻塞模式中,若函数返回的是错误,其实 IO 操作还并未执行,真正执行 IO 时,还是阻塞的。

IO 操作的分类

我们平时熟知的 socket 操作大多是同步 IO:

  1. 同步:read/write/recvfrom/sendto

  2. 异步: aio_read

关于 select/poll/epoll + 非阻塞:

仍然是同步 IO,在实际进行 IO 操作时,是阻塞式的。

引经据典

Richard Stevens 在 UNPv1 中是这样描述同步和非同步的:

POSIX defines these two terms as follows:

  • A synchronous I/O operation causes the requesting process to be blocked until that I/O operation completes.
  • An asynchronous I/O operation does not cause the requesting process to be blocked.