複製鏈接
請複製以下鏈接發送給好友

IOCP

鎖定
輸入輸出完成端口(Input/Output Completion Port,IOCP), 是支持多個同時發生的異步I/O操作的應用程序編程接口,在Windows NT的3.5版本以後,或AIX 5版以後或Solaris第十版以後,開始支持。
中文名
輸入輸出完成端口
外文名
Input/Output Completion Port
簡    稱
IOCP
性    質
應用程序編程接口
支持版本
Windows NT的3.5版本等
適用模型
C/S模式網絡服務器端模型

IOCP簡介

輸入輸出完成端口(Input/Output Completion Port,IOCP), 是支持多個同時發生的異步I/O操作的應用程序編程接口,在Windows NT的3.5版本以後,或AIX5版以後或Solaris第十版以後,開始支持。
IOCP特別適合C/S模式網絡服務器端模型。因為,讓每一個socket有一個線程負責同步(阻塞)數據處理,one-thread-per-client的缺點是:一是如果連入的客户多了,就需要同樣多的線程;二是不同的socket的數據處理都要線程切換的代價。 [1] 

IOCP原理

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

IOCP操作系統

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。 [1] 

IOCP內部結構

Jeffrey Richter説:“完成端口可能是最為複雜的內核對象”。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完成隊列非空,且工作線程並未超出總的併發數時,系統從等待線程隊列中取出線程,該線程從自身代碼的GetQueuedCompletoinStatus函數調用處返回並繼續運行。
  • 釋放線程隊列(Released Thread List):當操作系統從等待線程隊列中激活了一個工作線程時,或者掛起的線程重新被激活時,該線程被壓入釋放線程隊列中,也即這個隊列的線程處於運行狀態。這個隊列中的線程有兩個出隊列的機會:一是當線程重新調用GetQueuedCompeltionStatus函數時,線程被添加到等待線程隊列中;二是當線程調用其他函數使得線程掛起時,該線程被添加到“暫停線程隊列”中。
  • 暫停線程隊列(Paused Thread List):釋放線程隊列中的線程被掛起的時候,線程被壓入到“暫停線程隊列”中;當掛起的線程重新被喚醒時,從“暫停線程隊列”中取出放入到釋放線程隊列。 [1] 
參考資料
  • 1.    "Solaris 10 I/O Completion Ports". Archived from the original on July 19, 2011. Retrieved 2008-07-20.