轉載:Overlapped I/O,也就是asynchronous I/O——非同步I/O。
目前所有外設的速度(包括硬碟)跟CPU的速度相比仍然是天壤之別,而程式跟外設打交道又是非常正常的事情,因此如何讓程式既能進行I/O又不受制於 I/O速度瓶頸的限制是一大課題。而採取非同步方式則是一種自然而且有效方案。也就是說我們調用I/O函數後立即返回繼續往下執行,而不必等I/O完成後 再繼續,這樣就使得I/O操作與我們的程式之間能夠並發進行(從使用者的觀點看卻是並行的,看來感性的人類的感覺還是比較容易受欺騙的-_-)。當然,通常 我們都需要知道I/O的執行情況,包括其執行結果。從這個意義上講,非同步去執行的I/O過程跟我們釋放出去的一個線程在概念上是比較相似的。
在win32中,我們可有以下幾種方式(或者叫做機制)來實現Overlapped I/O。
1,激發的檔案handle
預設操作:
——用FILE_FLAG_OVERLAPPED參數調用CreateFile()函數以便告知被開啟的檔案以後要進行Overlapped I/O。
——定義一個OVERLAPPED結構並適當填充(hEvent域為空白)
執行操作:
——以定義的那個OVERLAPPED結構的地址為參數調用ReadFile()或WriteFile()來進行Overlapped I/O。
等待操作:WaitForSingleObject(), WaitForMultipleObjects(), MsgWaitForMultipleObjects()
等待對象:檔案handle
取得結果:GetOverlappedResult()
缺陷:
不 能方便地從檔案操作級獲知結果。用GetOverlappedResult()得到的只是最近一個該檔案上的操作的結果,要想獲得在該檔案上的每個操作的 結果必須在每個操作後用GetOverlappedResult()來擷取結果。這樣做效率比較低,也增加了程式的複雜性,降低了程式的靈活性。
2、激發的Event對象
預設操作:
——用FILE_FLAG_OVERLAPPED參數調用CreateFile()函數以便告知被開啟的檔案以後要進行Overlapped I/O。
——定義一個OVERLAPPED結構並適當填充(hEvent域為一個手動激發的Envet對象)
執行操作:
——以定義的那個OVERLAPPED結構的地址為參數調用ReadFile()或WriteFile()來進行Overlapped I/O。
等待操作:WaitForMultipleObjects(), MsgWaitForMultipleObjects()
等待對象:所關心的操作對應的Event對象
取得結果:Wait函數的返回結果
缺陷:
——等待多個對象的Wait函數對所等待的對象數目有限制MAXIMUM_WAIT_OBJECTS(比如64個)。
——必須不斷地根據Wait函數的返回結果來判斷是哪個Event對象被激發,從而作出相應的反應。
3、非同步程序呼叫(Asynchronous Precudure Call,APC)
預設操作:
——用FILE_FLAG_OVERLAPPED參數調用CreateFile()函數以便告知被開啟的檔案以後要進行Overlapped I/O。
——定義一個OVERLAPPED結構並適當填充(hEvent域不是必要的,此時可以用來存放一個使用者自訂結構的指標)
——定義一個回呼函數
執行操作:
——以定義的那個OVERLAPPED結構的地址以及回呼函數地址為參數調用ReadFileEx()或WriteFileEx()來進行Overlapped I/O。
等待操作:不必等待
等待對象:不必等待
取得結果:通過一個OVERLAPPED指標傳給回呼函數
缺陷:
——有好幾個I/O API並不支援APC,比如listen(), WaitCommEvent()
——只有發出Overlapped I/O的那個線程才能提供對應的回呼函數,這樣不利於構建可擴充系統
4、I/O Completion Ports
說明:
I/O Completion Ports提供了這樣一種機制:將一堆線程和一堆檔案handle關聯起來,使得這堆handle中的任何一個被激發時都會導致那堆線程中的一個被啟用 (如果存在的話)並為之服務,完成服務後的線程不會被結束,而是繼續回到那堆線程中,繼續等待被啟用。其實這就是線程池的概念。
另外,這堆線程只能用作這樣一個單一用途,除此之外不能去做其他任何事情。
預設操作:
——用FILE_FLAG_OVERLAPPED參數調用CreateFile()函數以便告知被開啟的檔案以後要進行Overlapped I/O。
——產生一個I/O Completion Port對象:CreateIoCompletionPort()
——將該對象和檔案handle關聯起來:CreateIoCompletionPort()
——產生一堆線程:CreateThread()
——讓每一個線程都在該I/O Completion Port對象上等待:GetQueuedCompletionStatus()
執行操作:
—— 對那些檔案handle進行一些Overlapped I/O。ConnectNamePipe(), DeviceIoControl(), LockFileEx(), ReadFile(), TransactNamePipe(), WaitCommEvent(), WriteFile()
等待操作:GetQueuedCompletionStatus()
等待對象:I/O Completion Port packet
取得結果:通過GetQueuedCompletionStatus()的參數傳遞給服務線程
缺陷:
【附】
——所謂可擴充(scalable)系統,是指能夠藉著增加RAM或磁碟容量或CPU個數而提升系統效能的系統。
——系統可能會讓Overlapped I/O立即執行的情況:
————請求的資料量比較小
————系統可以立即滿足請求(比如請求的資料已經在緩衝)
——系統肯定會讓Overlapped I/O立即執行的情況:
————進行一個寫入操作而造成檔案的擴充
————讀寫一個壓縮檔
——不讓系統激發I/O Completion Ports的方法:
在對已經跟某I/O Completion Port關聯的檔案進行操作時,產生一個Event對象用以填充其OVERLAPPED參數的hEvent域,該Event對象是手動重設的,並且其handle的最低位為1。