《Windows核心編程 5th》部分讀書筆記----第10章 同步裝置I/O與非同步裝置I/O

來源:互聯網
上載者:User

非同步裝置I/O基礎

假設一個線程向裝置發出一個非同步I/O請求。這個請求被傳給裝置驅動程式,後者負責完成實際I/O的操作。當驅動程式在等待裝置響應的時候,應用程式的線程並沒有因為要等待I/O請求完成而被掛起,線程會繼續運行並執行其他有用的任務。到了某一時刻,裝置驅動程式完成了對隊列中的I/O請求的處理,這時它必須通知應用程式資料已發送,資料已收到,或發生了錯誤,這些通知稱之為“接收I/O請求完成通知


接收I/O請求完成通知

用來接收I/O完成通知的方法

技術

摘要

觸發裝置核心對象

當向一個裝置同時發出多個I/O請求的時候,這種方法沒什麼用。它允許一個線程發出I/O請求,另一個線程對結果進行處理。

觸發事件核心對象

這種方法允許我們向一個裝置同時發出多個I/O請求。它允許一個線程發出I/O請求,另一個線程對結果進行處理。

使用可提醒I/O

這種方法允許我們向一個裝置同時發出多個I/O請求。發出I/O請求的線程必須對結果進行處理

使用I/O完成連接埠

這種方法允許我們向一個裝置同時發出多個I/O請求。它允許一個線程發出I/O請求,另一個線程對結果進行處理。這項技術具有高度的伸縮性和最佳的靈活性。


觸發裝置核心對象

當ReadFile和WriteFile函數在將I/O請求添加到隊列之前,會先將裝置核心對象設為未觸發狀態。當裝置驅動程式完成了請求之後,驅動程式會將裝置核心對象設為觸發狀態。因此我們可以利用裝置核心對象進行線程同步。


觸發事件核心對象

非同步I/O中的OVERLAPPED結構中的最後一個成員hEvent用來標識一個事件核心對象。當一個非同步I/O請求完成的時候,裝置驅動程式會檢查OVERLAPPED結構的hEvent成員是否為NULL。如果hEvent不為NULL,那麼驅動程式會調用SetEvent來觸發事件。與等待裝置核心對象類似,我們可以等待OVERLAPPED結構中的hEvent事件對象。



可提醒I/O

當系統建立一個線程的時候,會同時建立一個與線程相關聯的隊列。這個隊列被稱為非同步程序呼叫(asynchronous procedure call,APC)隊列。當發出一個I/O請求的時候,我們可以告訴裝置驅動程式在調用線程的APC隊列中添加一項。為了將I/O完成通知添加到線程的APC隊列中,我們應該調用ReadFileEx和WriteFileEx函數:

BOOL ReadFileEx(HANDLE hFile,PVOID pvBuffer,DWORD nNumBytesToRead,OVERLAPPED* pOverlapped,LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine) ;BOOL WriteFileEx(HANDLE hFile,CONST VOID *pvBuffer,DWORD nNumBytesToWrite,OVERLAPPED* pOverlapped,LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine) ;

上面兩個函數都包含一個最重要的參數,它要求我們傳一個回呼函數的地址,這個回呼函數被稱為完成函數(completion routine)。它的原型必須符合以下形式:

VOID WINAPI CompletionRoutine(DWORD dwError,DWORD dwNumBytes,OVERLAPPED * po) ;

當線程處於可提醒狀態的時候(調用Windows提供的6個函數,可以將線程置於可提醒狀態),系統會檢查它的APC隊列,對隊列中的每一項,系統會調用完成函數,並傳入I/O錯誤碼,已傳輸的位元組數,以及OVERLAPPED結構的地址。在調用那6個函數時,需要注意的是,當調用這些函數中的任何一個時,只要線程的APC隊列中至少有一項,線程就不會進入睡眠狀態。在調用這些函數的時候,若且唯若線程APC隊列中一項都沒有的時候,這些函數才會將線程掛起。當線程被掛起的時候,如果我們正在等待的那個(或那些)核心對象被觸發,或線程的APC隊列中出現了一項,那麼線程將會被喚醒。因為我們的線程處於可提醒狀態,所以一旦APC隊列中出現一項,系統會喚醒我們的進程並(通過調用回呼函數來)清空隊列。然後函數會立即返回---線程不會再次進行睡眠狀態來等等核心對象被觸發。


可提醒I/O的優劣

  • 回呼函數:可提醒I/O要求我們必須建立一個回呼函數,這使得代碼的實現變得更加複雜。由於這些回呼函數一般來說並沒有足夠的與某個問題有關的上下文資訊,因此我們最終不得不將大量的資訊放在全域變數中
  • 線程問題:實際上可提醒I/O的大問題是:發現I/O請求的線程必須同時對完成通知進行處理。如果一個線程發出多個請求,那麼即使其他線程完全處於空閑狀態,該線程也必須對每個請求的完成通知做出響應。由於不存在負載平衡機制,因此應用程式的伸縮性不太好。


I/O完成連接埠

1、建立I/O完成連接埠

I/O完成連接埠背後的理論是並發啟動並執行線程的數量必須有一個上限--也就是說,同時發出的500個客戶請求不應該允許出現500個可啟動並執行線程。一旦可運行線程的數量大於可用的CPU數量,系統就必須花時間來執行線程上下切換,而這會浪費寶貴的CPU周期--這也是並行存取模型的一個潛在缺點。

並行存取模型的另一個缺點是需要為每個客戶請求建立一個新的線程,這樣仍然會花費一定的開銷。如果能在應用程式初始化的時候建立一個線程池,並讓線程池中的線程在應用程式運行期間一直保持可用狀態,那麼服務應用程式的效能就能夠得到提高。I/O完成連接埠的設計初衷就是與線程池配合使用。

2、I/O完成連接埠的周邊架構

當服務應用程式初始化的時候,應用程式接著應該建立一個線程池來處理客戶請求。就標準的經驗法則而言,線程池中線程的數量應取主機的CPU的數量並將其乘以2

3、I/O完成連接埠如何管理線程池(工作機理)

首先,當我們建立I/O完成的時候,需要指定允許多少個線程並發運行。正如前面已經提到過,我們通常會將這個值設為主機的CPU數量(和線程池裡面有多少的線程不一樣)。當已完成的I/O項被添加到隊列中的時候,I/O完成連接埠想要喚醒正在等待的線程。但是,完成連接埠喚醒的線程數量最多不會超過我們指定的數量。因此,如果有4個I/O請求已完成,有4個線程正在等待GetQueuedCompletionStatus,那麼I/O完成連接埠只會喚醒兩個線程,而讓其他兩個線程繼續睡眠。當每個線程處理完一個已完成的I/O項時,會再次調用GetQueuedCompletionSatus。這時系統發現隊列中還有其他的項,於是會喚醒同一個線程來對剩餘的項進行處理。

當完成連接埠喚醒一個線程的時候,會將該線程的線程標識符儲存在與完成連接埠相關聯的第4個資料結構中,也就是已釋放線程列表(released thread list)。這使得完成連接埠能夠記住哪些線程已經被喚醒,並監視它們的執行情況。如果 一個已釋放的線程調用的任何函數將該線程切換到了等待狀態,那麼完成連接埠會檢測到這一情況,此時它會更新內部的資料結構,將該線程的線程標識符從已釋放線程列表中移除,並將其添加到已暫停線程列表(paused
thread list)中。

完成連接埠的目標是根據在建立完成連接埠時指定的並發線程的數量,將儘可能多的線程保持在已釋放線程列表中。如果一個已釋放線程由於任何原因而進入等待狀態,那麼已釋放線程列表會縮減,完成連接埠就可以釋放另一個正在等待的線程。如果一個已暫停線程被喚醒,那麼它會離開已暫停線程列表並重新進入已釋放線程列表。這意味著此時已釋放線程列表中的線程數量將大於最大允許的並發線程數量。

假設我們在一台有兩個CPU的機器上運行。我們建立一個同時最多隻允許兩個線程被喚醒的完成連接埠,還建立了4個線程來等待已完成的I/O請求。如果3個已完成的I/O請求被添加到連接埠的隊列中,只有兩個線程會被喚醒來對請求進行處理,這降低了可啟動並執行數量,並節省了環境切換的時間。現在,如果一個可運行線程調用了Sleep、WaitForSingleObject、WaitForMultipleObjects、SignalObjectAndWait,一個非同步I/O調用或任何能夠導致線程不可運行狀態的函數,I/O完成連接埠會檢測到這一情況並立即喚醒第3個線程。完成連接埠的目標是使CPU保持在滿負荷狀態下工作


簡單總結一下

IOCP的機制大概就是:當有一個線程調用GetQueuedeCompletionStatus去等待完成隊列中一項時,系統會將該線程添加到IOCP的等待線程隊列中。當I/O完成隊列中出現一項,系統會先喚醒最近等待的線程(LIFO),並將該線程添加到已釋放線程隊列中。當I/O完成隊列中繼續出現一項時,如果沒有線程池中還有線程在等待,且已釋放的線程沒有大於建立完成連接埠設定的數量,IOCP則會喚醒新的線程去處理新一項。否則等待已在啟動並執行線程處理完前一項,然後再讓處理完資料的線程來處理I/O完成隊列中這新的一項(這樣做可以減少不同線程進行環境切換帶來的花銷)。當一個正在啟動並執行線程調用其它函數,將自己掛起或處於等待狀態,IOCP會發現這種情況,並將這些線程放到自己的“已暫停線程隊列”中,也就是說,已釋放的線程數減少了,當I/O完成隊列中出現新的一項時,IOCP可以喚醒一個線程。與ICOP相關聯的4個資料結構(黑體字),大概就是這樣運作的。由此可以看出,IOCP既能讓CPU處理滿負荷狀態工作,也可以儘可能減少線程間切換上下文帶來的花銷。





相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.