Windows異步I/O和完成端口
上周做了一次關于Windows異步I/O和完成端口的部門技術分享,著重于理論介紹, 順帶review基于IOCP的網絡庫代碼。
完成端口是異步I/O的一種,將這兩個并列作為標題,因為完成端口的復雜性及用途相比其他幾種異步I/O加起來還過之。Windows核心編程中關于設備異步I/O介紹的很明白,這里再回顧一下。
先區分兩組概念:同步/異步(Async/Sync),阻塞/非阻塞(blocking/non-blocking)
這兩組概念很曖昧,有時可以不用分的很清楚。我將這兩組概念分別定性為面向結果和面向過程。想象生活中排隊的場景。印象中排隊最痛苦的是2010那年在上海觀看世博會,每進一個場館要排上2-5個小時,而且天氣燥熱,排隊過程中異常難耐。另外一種進場館的方式就是憑預約票,可以在當天按票上時間段進場。排隊進館就是一種同步方式,直到隊伍中輪到你才可以進去,憑票進館則是異步方式,兩種方式都是面向同一個結果:進館。但是排隊過程中,只能在隊伍中等待,意味著行為失去自由,也就是阻塞,什么事也做不了;而憑票則可以在進館之前到任何其他想去的地方,行動自由,也就是非阻塞的。從這個例子中可以看出為什么喜歡異步和非阻塞了,體現在程序中,則是它們的性能差別(可以看這篇)。現在開始介紹異步I/O。
一、請求異步I/O的條件
任何異步I/O都需要有系統設施的支撐,正如取票進館需要有預約票作憑證。請求異步I/O也需要兩個基本條件:隊列和重疊結構(OVERLAPPED,重疊是指I/O請求的時間和線程執行其他任務的時間是重疊的)。
typedef struct _OVERLAPPED { ULONG_PTR Internal; // I/O請求錯誤碼 ULONG_PTR InternalHigh; // 已傳輸字節數 DWORD Offset; // I/O操作位置(低、高位),非文件設備忽略 DWORD OffsetHigh; HANDLE hEvent; // 內核事件對象或自定義結構 }OVERLAPPED;
隊列由設備驅動程序維護,記錄I/O請求信息,其中包含數據地址和重疊結構的信息。
正是這個隊列和重疊結構,設備知道要將什么數據寫到什么位置上去或從什么位置讀取數據。并通知完成結果給程序。通知方式的不同決定了它們的性能差異,同樣,每種方式都需要不同的系統設施支持。
二、接收完成通知方式
1、觸發設備內核對象
- 工作機制
線程觸發一個異步I/O請求→設備內核對象被設為未觸發狀態→線程繼續執行其他任務,直到某個點線程開始等待,設備完成I/O操作,設備內核對象被設為觸發狀態→程序收到I/O完成。
- 硬件設施
設備內核對象(句柄)。
- 局限
只能同時執行單個I/O請求。
2、觸發事件內核對象
- 工作機制
線程設置事件內核對象(未觸發狀態),觸發一個異步I/O請求→線程繼續執行其他任務,直到某個點線程開始等待,設備完成I/O操作,事件內核對象被設為觸發狀態→程序收到I/O完成。
- 硬件設施
事件內核對象(句柄)。
- 局限
事件內核對象有限,不能滿足大量I/O請求。
3、可提醒I/O
- 工作機制
在使用可提醒I/O時,系統為程序發起I/O的線程創建了一個APC(Async Procedure Call)隊列,在設備完成I/O操作時,驅動程序將操作結果放入該隊列,其中包含程序傳遞給系統的完成回調函數和重疊結構,對程序來說,該隊列是透明的。此時線程還不知道是否IO完成。在線程進入可提醒狀態(調用了SleepEx及Wait*等函數),系統遍歷隊列,取出完成通知,然后讓線程執行完成回調函數。
- 硬件設施
- 局限
誰請求誰處理完成通知,其他線程一直閑著沒活干。
4、I/O完成端口
- 是什么?
一種線程池機制(MSDN)
一種絕佳的線程間通信機制(Windows核心編程)
一種線程同步對象
- 做什么?
密匙掌握在你手上!
- 硬件設施
I/O完成端口包含大量的主題可以討論,尤其應用在高性能易伸縮服務器上,以后再詳細討論。