跳至內容

IOCP

維基百科,自由的百科全書

輸入輸出完成埠(Input/Output Completion Port,IOCP), 是支持多個同時發生的異步I/O操作的應用程式編程接口,在Windows NT的3.5版本以後[1],或AIX 5版以後[2]Solaris第十版以後,開始支持。

IOCP特別適合C/S模式網絡伺服器端模型。因為,讓每一個socket有一個執行緒負責同步(阻塞)數據處理,one-thread-per-client的缺點是:一是如果連入的客戶多了,就需要同樣多的執行緒;二是不同的socket的數據處理都要執行緒切換的代價。

原理

[編輯]

通常的辦法是,執行緒池中的工作執行緒的數量與CPU內核數量相同,以此來最小化執行緒切換代價。一個IOCP對象,在作業系統中可關聯著多個Socket和(或)文件控制端。 IOCP對象內部有一個先進先出(FIFO)隊列,用於存放IOCP所關聯的輸入輸出端的服務請求完成消息。請求輸入輸出服務的進程不接收IO服務完成通知,而是檢查IOCP的消息隊列以確定IO請求的狀態。 (執行緒池中的)多個執行緒負責從IOCP消息隊列中取走完成通知並執行數據處理;如果隊列中沒有消息,那麼執行緒阻塞掛起在該隊列。這些執行緒從而實現了負載均衡。

Windows作業系統

[編輯]

IOCP是唯一一個不需要安全屬性的Windows內核對象。 這是因為IO完成埠在設計時就是只在一個進程中使用。

使用CreateIoCompletionPort函數創建一個新的IOCP,或把socket或文件句柄與一個已存在的IOCP關聯起來。

一個執行緒,第一次調用GetQueuedCompletionStatus函數時,該執行緒就成為關聯了該IOCP的執行緒,直到下述三種情形之一發生:

  • 該執行緒退出;
  • 該執行緒調用GetQueuedCompletionStatus函數關聯到其他的IOCP;
  • 該IOCP被關閉。

即,一個執行緒在任何時刻最多關聯一個IOCP。

執行緒調用GetQueuedCompletionStatus函數等待放入IOCP的I/O完成包(completion packet)。IOCP擁有一個執行緒池。阻塞在IOCP上的執行緒按照後進先出(LIFO)順序被釋放(這是為了減少執行緒切換的代價);而一個執行緒的完成包按照先進先出(FIFO)順序從IOCP的隊列中取走。IOCP有一個最大允許並發的執行緒數量上限,在CreateIoCompletionPort函數中指定,每次I/O完成包在從隊列取走前檢查關聯於該IOCP且正在並發執行的執行緒數量是否達到該限。因其他原因(如調用SuspendThread函數)而掛起的執行緒不算作正在執行的執行緒。CompletionKey(完成鍵)一般作為「單句柄數據」的結構體(PER_HANDLE_DATA),用來標識是哪個設備的I/O完成操作已經完成。IO重疊結構(Overlapped)一般作為「單IO數據」的結構體(PER_IO_DATA),該結構體的第1個成員為OVERLAPPED結構體,用來標識是設備的具體哪個操作。

執行緒可以用PostQueuedCompletionStatus函數在IOCP上投寄一個完成包。

關閉IOCP之前,必須先關閉關聯在該IOCP之上的所有File Handle或socket。

內部結構

[編輯]

Jeffrey Richter英語Jeffrey Richter說:「完成埠可能是最為複雜的內核對象」。[3] Windows中利用CreateIoCompletionPort命令創建完成埠對象時, 作業系統內部為該對象自動創建了5個資料結構,分別是:

  • 設備列表(Device List): 每當調用CreateIoCompletionPort函數時,作業系統會將該設備句柄添加到設備列表中;每當調用CloseHandle關閉了某個設備句柄時,系統會將該設句柄從設備列表中刪除
  • IO完成請求隊列(I/O Completion Queue-FIFO):當I/O請求操作完成時,或者調用了PostQueuedCompeltionStatus函數時,作業系統會將I/O請求完成包添加到I/O完成隊列中。當作業系統從完成埠對象的等待執行緒隊列中取出一個工作執行緒時,作業系統會同時從I/O完成隊列中取出一個元素(I/O請求完成包。
  • 等待執行緒隊列(WaitingThread List-LIFO):當執行緒中調用GetQueuedCompletionStatus函數時,作業系統會將該執行緒壓入到等待執行緒隊列中。為了減少執行緒切換,該隊列是LIFO。當I/O完成隊列非空,且工作執行緒並未超出總的並發數時,系統從等待執行緒隊列中取出執行緒,該執行緒從自身代碼的GetQueuedCompletionStatus函數調用處返回並繼續運行。
  • 釋放執行緒隊列(Released Thread List):當作業系統從等待執行緒隊列中激活了一個工作執行緒時,或者掛起的執行緒重新被激活時,該執行緒被壓入釋放執行緒隊列中,也即這個隊列的執行緒處於運行狀態。這個隊列中的執行緒有兩個出隊列的機會:一是當執行緒重新調用GetQueuedCompletionStatus函數時,執行緒被添加到等待執行緒隊列中;二是當執行緒調用其他函數使得執行緒掛起時,該執行緒被添加到「暫停執行緒隊列」中。
  • 暫停執行緒隊列(Paused Thread List):釋放執行緒隊列中的執行緒被掛起的時候,執行緒被壓入到「暫停執行緒隊列」中;當掛起的執行緒重新被喚醒時,從「暫停執行緒隊列」中取出放入到釋放執行緒隊列。

參考資料

[編輯]
  1. ^ Windows I/O Completion Ports. [2014-04-11]. (原始內容存檔於2014-03-20). 
  2. ^ Configuring IOCP on AIX 5 and 6. [2014-04-11]. (原始內容存檔於2013-06-30). 
  3. ^ 《Windows via C/C++》 Fifth Edition (December 2007) (Jeffrey Richter and Christophe Nasarre) Page292.

外部連結

[編輯]