前言:
在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 上的讀操作包含兩個階段:
- 等待資料準備好;
- 将資料從核心拷貝到程序中。
上述幾種IO模型就是在這兩個階段上各有不同的情況。
1.1 阻塞式IO
預設情況下,Linux下的所有socket都是阻塞的。以 UDP 的
recvfrom
調用為例:
當程序調用
recvfrom
時,該函數直到①資料報到達且被複制到應用程序緩沖區;②或者發生錯誤(比如被信号中斷)才傳回。
是以,阻塞式IO的特點就是在I/O執行的兩個階段都被阻塞了——阻塞等待資料,阻塞拷貝資料。
1.2 非阻塞式IO
Linux下可以通過
fcntl
将 socket 設定為非阻塞模式。
當對一個非阻塞 socket 執行讀操作時,如果核心中的資料還沒有準備好,那麼它并不會阻塞使用者程序,而是立刻傳回一個
EWOULDBLOCK
錯誤;如果核心中有資料準備好了,它會立即将資料拷貝到使用者記憶體,并成功傳回。
由于非阻塞I/O在沒有資料時會立即傳回,故使用者程序通常需要循環調用
recvfrom
,不斷地主動詢問核心資料是否ready。
是以,非阻塞式IO的特點是在I/O執行的第一個階段不會阻塞線程,但在第二階段會阻塞。
1.3 IO複用
IO複用(IO multiplexing),也稱事件驅動IO(event-driven IO),就是在單個線程裡同時監控多個套接字,通過 select 或 poll 輪詢所負責的所有socket,當某個socket有資料到達了,就通知使用者程序。
可以看出,程序阻塞在
select
調用上,等待有套接字變為可讀;當有套接字可讀以後,調用
recvfrom
把資料報從核心複制到使用者程序緩沖區,此時程序阻塞在IO執行的第二個階段。
如上圖整個使用者程序其實是一直被阻塞的,但IO複用的優勢在于可以等待多個描述符就緒。
是以,IO複用的特點是進行了兩次系統調用,程序先是阻塞在 select/poll 上,再是阻塞在讀操作的第二個階段上。
1.4 信号驅動式IO
信号驅動式IO(signal-driven IO),就是讓核心在描述符就緒時發送
SIGIO
信号通知使用者程序。
首先需要開啟 socket 的信号驅動式IO功能,然後通過
sigaction
系統調用注冊SIGIO信号處理函數 —— 該系統調用會立即傳回。當資料準備好時,核心會為該程序産生一個SIGIO信号,這時就可以在信号處理函數中調用 recvfrom 讀取資料了。
是以,信号驅動式IO的特點就是在等待資料ready期間程序不被阻塞,當收到信号通知時再阻塞并拷貝資料。
1.5 異步IO
異步IO(asynchronous IO)其實用得很少,在Linux 2.5 版本的核心中首次出現,在 2.6 版本的核心中才成為标準特性。
使用者程序在發起
aio_read
操作後,該系統調用立即傳回 —— 然後核心會自己等待資料ready,并自動将資料拷貝到使用者記憶體。整個過程完成以後,核心會給使用者程序發送一個信号,通知IO操作已完成。
異步IO與信号驅動式IO的主要差別是:信号驅動式IO是由核心通知我們何時啟動一個IO操作,而異步IO是由核心通知我們IO操作何時完成。
是以,異步IO的特點是IO執行的兩個階段都由核心去完成,使用者程序無需幹預,也不會被阻塞。
1.6 五種IO模型的比較
可以看出,前4種模型的主要差別在于第一階段,因為它們的第二階段是一樣的:都是阻塞于
recvfrom
調用,将資料從核心拷貝到使用者程序緩沖區。
二、阻塞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操作都交給核心來完成,核心完成後發信号通知,在此期間使用者程序完全不用去理會。