Study Notes - I/O Models
很久以前在研究 nginx 時,過程針對他的 I/O Model 有了初步的了解,但是追本朔源還是經典著作 UNIX Network Programming Chapter 6. I/O Multiplexing
,本文整理 BIO、NIO、AIO 等著名的 I/O Models 筆記。
W. Richard Stevens 美國電腦科學家,有多本經典著作,像是
UNIX Network Programming
、TCP/IP Illustrated 系列
。
名詞
只要是處理網路、Disk 有關的 I/O 應用,或者處理 Process / Thread 的問題,則會出現同步機制問題,很常會出現以下的名詞:
阻塞 (Blocking) 與非阻塞 (Non-Blocking)
阻塞 (Blocking) 與非阻塞 (Non-Blocking) 描述的是 請求
在等待結果時的 狀態
。
阻塞 (Blocking)
:調用的程序或者應用程式發起請求,在獲得結果之前,調用方的程序會懸 (Hang) 住不動,無法回應,直到獲得結果。非阻塞 (Non-Blocking)
:概念與阻塞相同,但是調用方不會因為等待結果,而懸著不動。後續通常透過輪詢機制 (Polling) 機制取得結果。
同步 (Synchronous) 與 非同步 (Asynchronous)
同步 (Synchronous) 與 非同步 (Asynchronous) 描述的是:使用者執行緒與 Kernel 的 通訊模式
。
同步 (Synchronous)
:使用者執行緒發出 I/O 請求後,要等待、或者輪詢 Kernel I/O 的操作完成後,才能繼續執行。- 等待 Kernel 回覆:Blocking IO,縮寫成
BIO
- 輪詢類似於 Non-Blocking IO,縮寫成
NIO
- 等待 Kernel 回覆:Blocking IO,縮寫成
非同步 (Asynchronous)
:或稱異步
,使用者執行緒發出 I/O 請求後仍然繼續執行下一個操作,當 Kernel I/O 操作結束後,會通知執行緒,或者呼叫 callback 函數。
同步
中文的意思很容易誤解為,很多事同時做,實際上是事情有 先後關係
的概念,也就是 有序性 (oredered)
;而 非同步
才是類似於很多事情在同一個時間一起發動,他是 無序性 (non-ordered)
。
I/O Models
利用 I/O Models 當關鍵字,可以找到很多篇文章在整理這些概念。下面的圖是我重新整理的 I/O Models 矩陣,依據 Blocking / Non-Blocking、Synchronize / Asynchronous 的組合,產生的四個象限如下圖:
搭配書本提到的五種方式:
Blocking I/O model
(Blocking / Synchronous): BIO,阻塞 I/ONon-Blocking I/O
(Non-Blocking / Synchronous): NIO,非阻塞 I/OI/O Multiplexing
(Blocking / Asynchronous): I/O 多工 (多路複用)Signal-driven I/O
(SIGIO): 訊號驅動,屬於 Synchronous I/OAsynchronous I/O
(Non-Blocking / Asynchronous): AIO- 在 Java7 裡面稱為 NIO2
一次的 Network I/O 都會涉及到兩個層次:先是呼叫此 I/O 應用程式的 Process / Thread,再來就是 Kernel。一個 read 動作會有以下兩個階段:
- 等待資料準備好 (Waiting for the data to be ready).
- This involves waiting for data to arrive on the network. When the packet arrives, it is copied into a buffer within the kernel.
- 把資料從 Kernel 複製到 Process (Copying the data from the kernel to the process).
- This means copying the (ready) data from the kernel’s buffer into our application buffer
這兩個階段的些微差異,產生了這五種 I/O Models。
依照我自己的理解,重新繪製了他們的時序圖,然後用我自己的語言重新解釋圖中的意思,整理如下。
1. Blocking I/O Model (BIO)
阻塞發生在執行緒 (Thread) 處於執行中,無法處理其他任務造成的現象,除非滿足特定條件讓 Thread 繼續做事。最常見的例子就是寫入資料的操作,需要等到 I/O 結束,才能繼續下一個動作。
Blocking I/O 的特性:
- 應用程式的程序不會詢問 Kernel 資料是否已經準備好了,直到 Kernel 回覆給應用程式。
- Java 的 FileInputStream、FileOutputStream、Socket R/W 都是 BIO.
2. Non-Blocking I/O (NIO)
執行過程與 BIO 一樣,如果無法完成,則返回 EAGIN
或 EWOULDBLOCK
。
Non-Blocking I/O 的特性:
- 應用程式的程序會不斷的詢問 Kernel 資料是否已經準備好了。
- 抽象概念來講,NIO 等同於 AIO,只是 NIO 是透過輪詢 (Polling) 方式取得結果,AIO 則有更多選擇。
3. I/O Multiplexing
多工 (Multiplexing, 或翻譯成多路複用)
,在矩陣中屬於 Blocking、Asynchronous 的組合。
最常見的 I/O Multiplexing 實作:
- Linux:
select
、poll
、epoll
- FreeBSD: kqueue
- Windows: IOCP (Input/Output Completion Port)
- Java 的 NIO (java.nio.*) 實際上是一種 多工 I/O,概念就是由 Channel、Selector、Buffer 等抽象類別,Source: EPollSelectorImpl.java
- Windows 透過
IOCP (Input/Output Completion Port)
實踐,Source: Iocp.java
- Windows 透過
4. Signal-driven I/O
跟 Kernel 說,當資料準備好的時候,送一個 SIGIO 信號給 Application,稱為 Singal-Driven I/O。
5. Asynchronous I/O
- 應用程式的程序告訴 Kernel 做一個操作 (Operation),不等 Kernel 回覆,程序繼續執行。
- Kernel 完成整個操作,包含取的資料,複製到 Buffer 之後,通知程序 (deliver signal specified in aio_read)
相關:
Comparison of the I/O Models
最後整理原文中畫的一份比較表,前面五種都分成兩個階段,都會被 blocked,最後的 AIO 則一次處理兩個階段。
結論
程式語言的 I/O 通常都會依賴於 OS,例如:
- Java 不同平台的 JRE 使用不同方式實作 I/O Multiplexing
- Node.js 則依賴於 libuv
生活中的 I/O Model
解釋阻塞、非阻塞、同步、非同步很常用到便利商店排隊的例子。
可以這樣想像,Blocking 與否,代表著客人的狀態,而 Sync 與否代表店員的處理方式。
Blocking I/O
:一堆人去便利商店買咖啡,第一個人跟店員點完咖啡後,站在櫃檯前等待店員煮好咖啡,然後下一個接著點 …- Blocking: 客人只能在櫃台等待
- Sync: 店員忙完後,才給客人咖啡
Non-Blocking I/O
:一堆人去便利商店買咖啡,第一個人跟店員點完咖啡後,跑去逛商品,直到店員煮好咖啡通知他來拿。- Non-Blocking: 客人可以先去做其他事
- Async: 店員忙完後,才給客人咖啡
I/O Multiplexing
:店員會主動通知客人咖啡好了,但是客人還是站在櫃檯等- Blocking: 客人只能在櫃台等待
- Sync: 店員固定在櫃檯,另一個在煮咖啡
AIO
:- Non-Blocking: 客人去櫃檯點餐後,店員給號碼牌就離開做其他事
- Async: 店員離開櫃檯去煮咖啡,另一個店員繼續在櫃台點餐
- 咖啡好了,叫號領咖啡。
比較 epoll, select, poll, kqueue
底下這張圖是 libevent 跑出來 的 benchmark:
Source: http://daemonforums.org/showthread.php?t=2124
後記
這份筆記只是我自己重新整理 UNIX Network Programming Chapter 6. I/O Multiplexing
的圖,加上我自己的理解,所以難免有些錯誤理解,或者圖畫錯的部分。如果有發現錯誤,歡迎直接回饋給我,感謝。
圖形的 原始檔 歡迎不吝指正。
延伸閱讀
參考資料
- Chapter 6. I/O Multiplexing: The select and poll Functions
- Boost application performance using asynchronous I/O
- select、poll、epoll之间的区别总结[整理]
- Thread Pools in NGINX Boost Performance 9x! - Nginx Event Loop
- Inside NGINX: How We Designed for Performance & Scale
- libevent
- Java 核心技術 36 講
- Windows 95 的都市傳說獲得證實,亂動滑鼠確實能讓系統加速!