天天看點

IO模型:同步、異步、阻塞、非阻塞

前言:

在Linux的網絡程式設計中,同步IO(synchronous IO)、異步IO(asynchronous IO)、阻塞IO(blocking IO)、非阻塞IO(non-blocking IO)究竟是什麼?它們之間又有什麼聯系和差別? 本文是我對這個問題的答案整理的筆記,參考《UNIX網絡程式設計.卷1》

一、IO模型

在《UNIX網絡程式設計.卷1》第6.2節介紹了五種IO模型,分别是:

  • 阻塞式IO(blocking IO)
  • 非阻塞式IO(non-blocking IO)
  • IO複用(IO multiplexing)
  • 信号驅動式IO(signal driven IO)
  • 異步IO(asynchronous IO)

通常一個 socket 上的讀操作包含兩個階段:

  1. 等待資料準備好;
  2. 将資料從核心拷貝到程序中。

上述幾種IO模型就是在這兩個階段上各有不同的情況。

1.1 阻塞式IO

預設情況下,Linux下的所有socket都是阻塞的。以 UDP 的

recvfrom

調用為例:

IO模型:同步、異步、阻塞、非阻塞

當程序調用

recvfrom

時,該函數直到①資料報到達且被複制到應用程序緩沖區;②或者發生錯誤(比如被信号中斷)才傳回。

是以,阻塞式IO的特點就是在I/O執行的兩個階段都被阻塞了——阻塞等待資料,阻塞拷貝資料。

1.2 非阻塞式IO

Linux下可以通過

fcntl

将 socket 設定為非阻塞模式。

IO模型:同步、異步、阻塞、非阻塞

當對一個非阻塞 socket 執行讀操作時,如果核心中的資料還沒有準備好,那麼它并不會阻塞使用者程序,而是立刻傳回一個

EWOULDBLOCK

錯誤;如果核心中有資料準備好了,它會立即将資料拷貝到使用者記憶體,并成功傳回。

由于非阻塞I/O在沒有資料時會立即傳回,故使用者程序通常需要循環調用

recvfrom

,不斷地主動詢問核心資料是否ready。

是以,非阻塞式IO的特點是在I/O執行的第一個階段不會阻塞線程,但在第二階段會阻塞。

1.3 IO複用

IO複用(IO multiplexing),也稱事件驅動IO(event-driven IO),就是在單個線程裡同時監控多個套接字,通過 select 或 poll 輪詢所負責的所有socket,當某個socket有資料到達了,就通知使用者程序。

IO模型:同步、異步、阻塞、非阻塞

可以看出,程序阻塞在

select

調用上,等待有套接字變為可讀;當有套接字可讀以後,調用

recvfrom

把資料報從核心複制到使用者程序緩沖區,此時程序阻塞在IO執行的第二個階段。

如上圖整個使用者程序其實是一直被阻塞的,但IO複用的優勢在于可以等待多個描述符就緒。

是以,IO複用的特點是進行了兩次系統調用,程序先是阻塞在 select/poll 上,再是阻塞在讀操作的第二個階段上。

1.4 信号驅動式IO

信号驅動式IO(signal-driven IO),就是讓核心在描述符就緒時發送

SIGIO

信号通知使用者程序。

IO模型:同步、異步、阻塞、非阻塞

首先需要開啟 socket 的信号驅動式IO功能,然後通過

sigaction

系統調用注冊SIGIO信号處理函數 —— 該系統調用會立即傳回。當資料準備好時,核心會為該程序産生一個SIGIO信号,這時就可以在信号處理函數中調用 recvfrom 讀取資料了。

是以,信号驅動式IO的特點就是在等待資料ready期間程序不被阻塞,當收到信号通知時再阻塞并拷貝資料。

1.5 異步IO

異步IO(asynchronous IO)其實用得很少,在Linux 2.5 版本的核心中首次出現,在 2.6 版本的核心中才成為标準特性。

IO模型:同步、異步、阻塞、非阻塞

使用者程序在發起

aio_read

操作後,該系統調用立即傳回 —— 然後核心會自己等待資料ready,并自動将資料拷貝到使用者記憶體。整個過程完成以後,核心會給使用者程序發送一個信号,通知IO操作已完成。

異步IO與信号驅動式IO的主要差別是:信号驅動式IO是由核心通知我們何時啟動一個IO操作,而異步IO是由核心通知我們IO操作何時完成。

是以,異步IO的特點是IO執行的兩個階段都由核心去完成,使用者程序無需幹預,也不會被阻塞。

1.6 五種IO模型的比較

可以看出,前4種模型的主要差別在于第一階段,因為它們的第二階段是一樣的:都是阻塞于

recvfrom

調用,将資料從核心拷貝到使用者程序緩沖區。

IO模型:同步、異步、阻塞、非阻塞

二、阻塞vs非阻塞,同步vs異步

回到本文開頭的那個問題:同步IO、異步IO、阻塞IO、非阻塞IO究竟是什麼?它們之間又有什麼聯系和差別?

阻塞IO vs 非阻塞IO

上面介紹阻塞式IO模型、非阻塞式IO模型時已經說明了兩者的差別:

  • 阻塞I/O會一直阻塞使用者程序直到操作完成
  • 非阻塞I/O在核心的資料還沒準備好的情況下會立即傳回

同步IO vs 異步IO

POSIX是這樣定義的:

  • A synchronous I/O operation causes the requesting process to be blocked until that I/O operation completes. —— 同步IO操作導緻程序阻塞,直到IO操作完成。
  • An asynchronous I/O operation does not cause the requesting process to be blocked. —— 異步IO操作不導緻程序阻塞。

上面定義中的

I/O operation

是指真正的I/O系統調用,比如

recvfrom

,是以阻塞式I/O模型、非阻塞式I/O模型、I/O複用模型、信号驅動式I/O模型都屬于同步I/O。—— 隻有異步I/O模型屬于POSIX定義的異步I/O,因為在異步I/O模型中,使用者程序是将整個I/O操作都交給核心來完成,核心完成後發信号通知,在此期間使用者程序完全不用去理會。