1. 程式人生 > >windows IOCP完成埠原理詳解

windows IOCP完成埠原理詳解

開篇之前先放上本次講的IOCP project github地址:這裡 。這個project中包含了IOCP和select,各自封裝成一個動態連結庫,可以直接使用。同時專案配有完整的glog支援,方便除錯,並可以通過config控制server。如有bug,歡迎大家提出,正在完善過程中,程式碼可以優化的地方也請大家隨時提出,一起進步成長。


本文主要從以下幾方面講解IOCP使用及其原理。
  • 為什麼需要完成埠
  • 完成埠能做什麼
  • 完成埠原理
  • 如何使用完成埠

1.  為什麼需要完成埠

網路通訊模型是編寫網路程式的一個比較核心的模組,也直接影響著程式的效能,所以選擇合適的網路模型是非常有必要的。
IOCP是一種網路通訊模型,但是在IOCP出現之前已經有相關網路通訊模型在使用了,比較普遍的應該就是select模型,另外windows自己家也單獨實現了alertable I/O等。但是提到的select和alertable I/O都存在一些侷限,比如select模型其併發處理量受FDSETSIZE巨集大小限制,在windows平臺上這個大小預設是64,當然也可以自己在包含select之前手動#define其值,但是如果在使用之前就定義一個很大的值難免有點造成資源浪費,libevent就提供了一種自由的方法來使用select,這個在後面的文章中會詳細介紹。alertable I/O一個缺陷是多執行緒之間無法達到負載均衡的,同一個執行緒發出的IO請求必須由同一個執行緒來接收,即使其他執行緒閒著沒事幹。所以這不能充分利多核系統的強大資源。那麼IOCP有沒有缺陷呢,當然也有,首先是使用起來不夠簡單明瞭,介面設計的不夠簡潔直觀。但是呢效能還是槓槓的。
上面簡單的說了IOCP模型與其他模型的一些對比,另外一點很大的區別是,IOCP模型是一種真正意義上的非同步通訊模型,具體啥是非同步啥是同步可以參考我之前的一篇文章。有一點需要說明的是,並不是所有網路通訊專案都必須要使用IOCP模型,對於一些已知的連線數較少的網路程式,完全可以用select甚至是每個客戶端對應一個執行緒這種方式。

2.  完成埠能做什麼

上面吹了一大波完成埠,那麼完成埠究竟能做什麼呢。
首先一點是:IOCP會主動幫我們完成網路IO資料複製。這一點其實也就是他與其他網路模型最直接的區別了,一般網路操作包括兩個步驟,以recv來說吧,如果是一般模型,那麼其第一步是通知等待的執行緒有資料可以讀取,這時候執行緒會呼叫recv或者recvfrom等函式將資料從讀緩衝區複製到使用者空間,然後再做下一步的處理,而IOCP能幫我們的是,他會在核心中幫我們監聽那些我們感興趣的的事件,例如我們希望接收客戶端資料,那麼我們向完成埠投遞一個讀事件,完成埠在監測有讀事件到來的時候會主動地去幫我們把資料從記憶體空間複製到使用者空間,然後通知我們過來取資料就OK了,這就是IOCP提供的方便之處。

另外一點:IOCP在內部管理執行緒,實現負載平衡。上面提到了windows的alertable I/O的負載均衡是他一個弊端,那麼IOCP是如何自己管理執行緒排程的呢,簡單的說就是以棧的方式進行管理,具體內容接下來一節會詳細描述。

3.  完成埠原理

  •  overlapped
提到IOCP就不得不提到overlapped這個資料結構,這個資料結構是IOCP進行非同步通訊的關鍵。
typedef struct _OVERLAPPED {
  ULONG_PTR Internal;
  ULONG_PTR InternalHigh;
   union {
    struct {
      DWORD Offset;
      DWORD OffsetHigh;
    };
    PVOID  Pointer;
  };
  HANDLE    hEvent;
} OVERLAPPED, *LPOVERLAPPED;
這個資料結構原本windows是不打算公開的,隨著軟體程式設計的發展後來微軟的工程師發現程式設計人員需要用到這個資料結構,所以就把他公開了,但是對於內部的變數名卻沒有變動,因為微軟內部使用這個變數名有太多地方了,如果該變數名可能會帶來很多其他的不好的影響,所以就沒有更換變數名。具體的變數含義在微軟的官方文件中已經明確給出。
Internal: 這個變數用來表明當前IO請求的狀態,當我們向完成埠提交一個IO請求的時候如果請求還沒有響應,這個值就會是STATUS_PENDING。
InternalHigh:這個變數用來當前IO請求位元組數。
Offset:指定檔案的起始偏移位置
OffsetHigh:指定開始傳輸資料的位元組數的的高位
hEvent:是事件控制代碼,在IO請求完成後處於訊號狀態。
需要注意的是,在網路通訊過程中offser與offserhigh是被系統自動忽略的,這兩個值在非同步讀寫檔案時使用。
在非同步IO過程中,只要向可以使用OVERLAPPED資料結構的函式投遞一個overlapped資料機構,系統核心就會在後臺默默的幫你監聽你所投遞的IO事件,當你的感興趣的事件觸發的時候系統會在後臺幫你收集好資料然後通知你事件完成,並返回給你你投遞的Overlapped資料結構的地址,由此可以看出在時間完成之前你是不能改變overlappd的地址的,否則會出現未定義的行為。
在專案中使用Overlapped資料結構一般有兩種方法:
① 使用結構體包含,並通過CONTAINING_RECORD抽取IO資料
struct TEST_OVERLAPPED{
OVERLAPPED overlapped_;
WSABUF  wsabuf_;
chardata[SIZE];
int data_len_;
OP operate_type_;
}
定義這樣的資料結構,在傳入overlapped函式的時候將其強制轉換成(OVERLAPPED*)(TEST_OVERLAPPED), 另外OVERLAPPED資料結構一定要放在新的資料結構頭部。operate_type_是自己定義的一個標識,用來表示這個IO請求是什麼型別的,當然也可以將本次IO請求的socket控制代碼放進去,用來表明具體是哪個client的IO。所有的這些資料都可以通過CONTAINING_RECORD巨集來抽取,具體CONTAINING_RECORD是如何工作的我就不細講了,網上一大堆。
② C++類繼承overlapped資料結構
這種方法其實與第一種方法很類似,不過比較方便的是不需要用CONTAINING_RECORD來抽取具體資訊了。
class MyOverlapped : public OVERLAPPED{
WSABUF wsabuf_;
chardata_[DATASIZE];
int data_len_;
OP operate_type_;
SOCKETclient_;
}
同樣在使用的時候需要將其轉換為(OVERLAPPED*)傳入overlapped函式,在IO事件完成後再將其轉換為我們的類,(MyOverlapped*)(&OVERLAPPED),之後直接讀取成員變數即可得出資訊。
  • IOCP內部工作原理

先上一張Jeffrey Richter在windows核心程式設計裡的一個IOCP原理圖。一張好圖的效果比說n句話效果要好多了。


雖然微軟沒有公開完成埠具體的實現方式,但是從Jeffrey Richter的windows核心程式設計可以大概瞭解完成埠的大概實現。

當我們建立一個完成埠的時候(建立方式下一節具體講)windows底層會幫我們建立一系列底層設施,以輔助我們後來的通訊過程。具體設施如上圖所示。
①裝置列表
這裡的裝置列表我們可以簡單的認為就是所有連線的socket資訊的列表,對於一個socket我們要將他與完成埠關聯,完成端口才會在核心中為我們監聽我們感興趣的事件,這裡有一個關鍵的資料結構,dwCompletionKey,這個資料結構需要在我們將socket繫結到完成埠時一起傳入核心裝置列表中,那麼他有什麼作用呢?我的理解是這個資料結構主要是為了在核心中標記當前所通訊的socket物件具體是哪一個,這個資料結構是由我們自己定義的,所以在這個結構體裡我們可以加上我們一些自己想要的資訊,因為後來通訊過程中核心會將這個資料傳送給我們,所以在這個結構中定義一些你感興趣的欄位可以方便後期的一些操作,比如你可以定義一個容器用來存放改裝置投遞的所有IO操作,這樣在後期該socket關閉的時候可以方便清理與其相關的記憶體,以確保不會造成記憶體洩漏。
②完成佇列
完成佇列中存放的是已經完成的IO事件,每一個列表項主要包括dwBytesTransferred, dwCompletionKey, pOverlapped, dwError四個資料,dwBytesTransferred顧名思義就是本次IO事件所傳輸的位元組數,dwCompletionKey就是上面我們所說的用來標識socket資訊的資料結構,從這個資料結構我們可以知道當前的IO事件是發生在哪個socket上的,當然你需要在completionKey中設定這個欄位,否則你也不知道這個事件是發生在哪個socket上的。pOverlapped資料結構也就是我們之前提到的Overlapped資料結構,這個資料結構裡包含了IO資料。
③執行緒管理設施
執行緒管理是完成埠的一大特點,也就是核心在內部自己管理一個執行緒池。與這個執行緒池相關的基礎設施主要有等待執行緒佇列(棧)(以棧的方式管理),已釋放的執行緒列表,已暫停的執行緒列表。當執行緒呼叫GetQueuedCompletionStatus的時候核心會將該執行緒放入執行緒棧中,因為GetQueuedCompletionStatus會將執行緒掛起,一旦有事件發生GetQueuedCompletion返回,這時候核心會將執行緒放入已釋放列表中,如果這個已釋放的執行緒又呼叫了一些函式將執行緒掛起,那麼核心會將其放入已暫停執行緒列表中。當GetQueuedCompletionStatus返回後執行緒處理完資料後再次呼叫GetQueuedCompletionStatus進行等待時,核心會重新將該執行緒放到執行緒等待棧中,這樣的一個流程下來我們可以看出,如果在IO事件處理比較慢的情況下一個執行緒就可以搞定所有的IO請求,這樣避免了執行緒之間的上下文切換帶來的效能開銷。

4. 如何使用完成埠

鋪墊了這麼多,終於要講到如何使用IOCP了。先介紹一下使用完成埠需要用到的幾個比較重要的函式。

(1)CreateIOCompletionPort

之前說到過完成埠在API設計上不夠清晰,現在提到的這個函式可以充分說明這個問題。這個函式有兩個用途。
①CreateIOCompletionPort(-1,NULL,NULL,0)
這種呼叫方法是用來建立一個新的完成埠時使用的方式,最重要的是第四個引數,第四個引數主要是用來確定在同一時間最多能有多少個執行緒執行,設定為0代表數量與機器CPU個數一致。
②CreateIOCompletionPort(socket, completionport, pcompletionkey, 0)
這種呼叫方式使用老將一個socket控制代碼與已建立好的完成埠相關聯,第一個引數代表控制代碼建立的時候不需要傳入,這個主要是用於將一個控制代碼繫結到完成埠時時使用的,第二個引數代表已建立好的完成埠,第三個引數completionley上面已經說過了,是用來標識一個socket控制代碼的。

(2)GetQueuedCompletionStatus(

  _In_  HANDLE       CompletionPort,
  _Out_ LPDWORD      lpNumberOfBytes,
  _Out_ PULONG_PTR   lpCompletionKey,
  _Out_ LPOVERLAPPED *lpOverlapped,
  _In_  DWORD        dwMilliseconds)
這個函式主要是將當前執行緒掛起等待IO事件完成。
CompletionPort:就是上面所建立的埠,另外有一點要說明的是這個埠與socket中使用的埠不是一個概念,你就把這個埠當成一個核心控制代碼就行。
lpNumberOfBytes:這個代表IO事件傳輸的位元組數
lpCompletionKey:代表當前IO事件所隸屬的控制代碼資訊,這個是我們在繫結控制代碼到完成埠時自己傳進去的。
lpOverlapped:這個值就包含了這次非同步IO的資料資訊。
dwMilliseconds:代表在等待一個IO事件完成時會等多久,如果這個值設為INFINITE,那麼這個呼叫將永遠不會超時,如果傳入0,那麼這個呼叫會立即返回。

(3)PostQueuedCompletionStatus(

  _In_     HANDLE       CompletionPort,
  _In_     DWORD        dwNumberOfBytesTransferred,
  _In_     ULONG_PTR    dwCompletionKey,
  _In_opt_ LPOVERLAPPED lpOverlapped
  )
  這個函式可以用來模擬IO完成事件,經常用於退出時傳送一個模擬的IO完成事件來喚醒在等待中的執行緒,引數資訊之前都有提到就不解釋了。
  以上三個就是使用完成埠時最主要的三個函式,那麼既然要用到Overlapped資料結構來投遞IO請求事件,那麼socket的傳送接收函式也就不能用原來常用的send和recv了,要用到WSAERecv,WSASend兩個函數了,因為這兩個函式都接收一個overlapped引數。另外還有一個要提到的是,accept沒有對應的WSA版本,但是由於accept是一個阻塞函式,所以如果想盡可能提高效能,可以使用微軟後期自己封裝的一個函式acceptex,這個函式與原始的accept之間的差別在於它是將已經建立好的socket傳入函式,那麼當其IO事件返回,對應的socket也就已經與客戶端建立連線了,這個函式也接受一個overlapped資料結構,所以我們可以把accept事件當做一般的IO事件即可,當GetQueuedCompletionStatus返回時檢查completionkey中的socket控制代碼是不是listen socket,如果是listen socket說明有新的連線接入,這時候需要呼叫微軟自己的函式GetAcceptExSockaddrs來獲取客戶端的地址資訊。在建立好完成埠時可以先投遞幾個accept IO事件,這樣可以在高併發的時候處理的得心應手,當然了,完成埠本身就很強大。有一點要注意的是在每次處理完新的連線時要重新投遞新的accept事件,為下一個連線做準備。
IOCP具體如何使用可以看我的GitHub,裡面有完整的完成埠專案,對overlapped與completionkey都做了封裝,有資源管理器,測試還未發現資源洩露,有問題可以直接提出來,會及時改進。
  完成埠使用流程圖:

相關推薦

windows IOCP完成原理

開篇之前先放上本次講的IOCP project github地址:這裡 。這個project中包含了IOCP和select,各自封裝成一個動態連結庫,可以直接使用。同時專案配有完整的glog支援,方便除錯,並可以通過config控制server。如有bug,歡迎大家提出,

九.ARM裸機學習之串列通訊1(串列通訊介面及原理框圖

一.串列埠通訊的基本概念及原理理解 2017/11/12 22:52 1.同步通訊和非同步通訊: 非同步通訊:指通訊的傳送與接收裝置使用各自的時鐘控制資料的傳送和接收過程。為使雙方的收發協調,要求傳送和接收裝置的時鐘儘可能一致,即傳送方和接收方沒有統一的時鐘節拍、而各自按照

磁盤陣列 RAID 技術原理

十分 單獨 很好 不同的 raid1 miss 和數 會同 帶寬 RAID一頁通整理所有RAID技術、原理並配合相應RAID圖解,給所有存儲新人提供一個迅速學習、理解RAID技術的網上資源庫,本文將持續更新,歡迎大家補充及投稿。中國存儲網一如既往為廣大存儲界朋友提供免費、精

解決ajax跨域的方法原理之Cors方法

詳細 不同 htm 渲染 jsonp del 需要 methods href 1、神馬是跨域(Cross Domain) 對於端口和協議的不同,只能通過後臺來解決。 一句話:同一個ip、同一個網絡協議、同一個端口,三者都滿足就是同一個域,否則就是 跨域問題了。而為

Nginx+Php-fpm運行原理

pop 圖片 ron 什麽 地址 pan webserver family tid 一、代理與反向代理 現實生活中的例子 1、正向代理:訪問google.com 如上圖,因為google被墻,我們需要vpnFQ才能訪問google.com。 vpn對於“我們”來說,是可

虛擬化技術基礎原理

虛擬化技術基礎原理詳解DISK : IO調度模式 CFQ deadline anticipatory NOOP/sys/block/<device>/queue/schedulerMemory: MMU TLB vm.swappiness={0..100},使用交換分區的

常用 JavaScript 小技巧及原理

this lin slice pen global 轉化 script lis fun 善於利用JS中的小知識的利用,可以很簡潔的編寫代碼 1. 使用!!模擬Boolean()函數 原理:邏輯非操作一個數據對象時,會先將數據對象轉換為布爾值,然後取反,兩個!!重復取反,就實

Storm概念、原理及其應用(一)BaseStorm

when 結構 tails 並發數 vm 虛擬機 cif 異步 優勢 name 本文借鑒官文,添加了一些解釋和看法,其中有些理解,寫的比較粗糙,有問題的地方希望大家指出。寫這篇文章,是想把一些官文和資料中基礎、重點拿出來,能總結出便於大家理解的話語。與大多數“wordc

主成分分析(PCA)原理(轉載)

增加 信息 什麽 之前 repl 神奇 cto gmail 協方差 一、PCA簡介 1. 相關背景 上完陳恩紅老師的《機器學習與知識發現》和季海波老師的《矩陣代數》兩門課之後,頗有體會。最近在做主成分分析和奇異值分解方面的項目,所以記錄一下心得體會。

lvs和keeplived的工作原理

lvs+keeplived的工作原理一、lvs的工作原理 使用集群的技術和liunx的操作系統實現一個高性能、高可用的服務器。可伸縮性、可靠性、很好的管理性。 特點:可伸縮網絡服務的幾種結構,它們都需要一個前端的負載調度器(或者多個進行主從備份)。我們先分析實現虛擬網絡服務的主要技術,指出IP負載均衡技術

js中幾種實用的跨域方法原理

自身 標簽 cdc 返回 屬性和方法 插入 實用 封裝 判斷 這裏說的js跨域是指通過js在不同的域之間進行數據傳輸或通信,比如用ajax向一個不同的域請求數據,或者通過js獲取頁面中不同域的框架中(iframe)的數據。只要協議、域名、端口有任何一個不同, 都被當作是不同

http原理

tor keep 接受 地址 lru structure 格式 dns 請求方式 1. HTTP簡介 HTTP協議(HyperText Transfer Protocol,超文本傳輸協議)是用於從WWW服務器傳輸超文本到本地瀏覽器的傳送協議。它可以使瀏覽器

HTTP2.0 原理

不依賴 href 漸進 pre new 進制 四個步驟 回來 stream 影響一個網絡請求的因素主要有兩個,帶寬和延遲。今天的網絡基礎建設已經使得帶寬得到極大的提升,大部分時候都是延遲在影響響應速度。連接無法復用連接無法復用會導致每次請求都經歷三次握手和慢啟動。三次握手在

css-浮動與清除浮動的原理(清除浮動的原理你知道嗎)

alt col ges mage all strong splay height http float元素A的特點: 脫離文檔流 靠向left或right float元素會和塊盒子重疊 準確來說,是塊盒子和A重疊,但塊盒子內容會浮動在A周圍 不會和inline元素重

VMware VDS原理--20170922

vmware vds原理詳解--20170922本文純屬個人理解,如有錯誤請留言指正。上圖為vSpere中一個簡單的結構拓撲圖,借用這個拓撲圖來解釋一下為什麽VDS能夠邏輯上跨越ESXi。首先說明一下整個架構1. 在底層用三臺ESXi表示一個底層的網絡,另外用Storage代表存儲,在最左邊ESXi中的最左邊

歸並排序原理!

elf 可能 動態空間 文件合並 you col 相同 治法 lte 無論在空間的利用上還是原理的簡介,使用空間換取時間的代價是必須的! 申請一定量的動態空間,double也是有可能!實際會有許多的問題。 時間復雜度,計算方法如下!因為每次比較都為( k*n/2 )+l*n

DPM(Deformable Part Model)原理(匯總)

特征向量 成就 算法思想 filter people tell 梯度 錨點 精度 寫在前面: DPM(Deformable Part Model),正如其名稱所述,可變形的組件模型,是一種基於組件的檢測算法,其所見即其意。該模型由大神Felzenszwalb在2008年提

交換機的原理

交換機的原理詳解實驗名稱:交換機 MAC 表形成 實驗需求: 1、確保 PC-1 與 PC-3 可以互相Ping通; 2、查看交換機的 MAC 地址表 ; 實驗步驟: 1、互聯交換機與 PC-1/2/3 ; 分別對應交換機的 Fas0/1/2/3口 2、配置PC-1/2/3的I

ping命令知識 Ping命令工作原理

pla bsp 知識 網吧 撥號 lock tcp 問題 mage   在網絡應用中,ping網速與IP地址等都是非常常用的命令,但大家知道ping命令的工作原理嗎?要知道這其中的奧秘,我們有必要來看看Ping命令的工作過程到底是怎麽樣的。下面介紹下ping命令的詳細知識。

常見sql註入原理

sql註入1、首先我們創建一個mysqli的鏈接/**數據庫配置*/$config = [‘hostname‘=>"localhost", ‘port‘=>"3306", ‘username‘=>"root",‘password‘=>‘‘,‘db‘=>‘sql‘];/**接收參數