裝置io的(同步非同步io,iocp/epool等),以及iocp 和socket操作(求批判|討論)

來源:互聯網
上載者:User

iocp/epool這類東西是用於與裝置通訊時擷取通知。

 

這裡的裝置在訪問硬碟上的檔案就是和檔案系統驅動打交道,訪問socket時則是和網路驅動(軟體->硬體)打交道。

 

os驅動運行模式一般是發送命令,等待命令完成,擷取結果。

 

層次類似這樣

user

-----------------------

kernel <=> driver

 

driver下面可以掛很多個,其具體實現跟使用者沒什麼關係, kernel會知道什麼時候可以認為操作實際完成。

曆史悠久的2種訪問方式

 

A. 阻塞訪問時(read/send),每次api call後,會等待操作完成再返回,此時使用者線程處於掛起狀態。白白浪費了處理器時間。

其工作模式(實際的通知模式等可能跟描述有差異,但流程上是這樣)

user->api -> 發送請求至kernel 掛起使用者線程

請求完成時

driver發送請求結果通知kernel,kernel 返回結果至api,恢複使用者線程。

 

B.非同步訪問:

user->api 根據使用者操作 查詢裝置讀寫狀態,根據狀態返回結果(成功返回資料,或失敗需要block)

 

這裡比較特殊的是讀。

例如訪問磁碟檔案,第一次讀請求肯定是沒資料的,因為kernel不知道你請求的是讀哪塊資料,不可能說開啟檔案,系統就馬上把檔案讀取到緩衝裡面。

所以讀檔案的第一次訪問,實際操作是發送讀取命令到driver,然後返回would block。

kernel收到返回資料後下次讀操作可以立刻完成。

 

對於可以主動產生資料的裝置(socket/串口之類),第一次讀就有可能立即完成。

 

-----------------------------------------------------------------------------------------------------------------------------------------

在裝置密集情況下:

同步模式顯然是很低效的,大部分cpu時間在等待裝置完成操作。

非同步模式要稍微好點,但是需要大量時間輪詢,裝置很多,io少時cpu消耗在查詢上面很大。

 

於是出現了selecl/pool模式用於查詢一堆裝置的讀寫狀態。然後標記可讀,或可寫的裝備供使用者操作。

 

在select/pool時每次需提交大量的裝置控制代碼(r/w/e控制代碼數組),而可能只返回其中一個,操作效率太低。

於是epool出現。

 

epool和select/pool不同在於,其關注的裝置列表是由系統核心維護 用epool_ctrl來增減修改關注裝置或需要查詢的狀態。

因此每次查詢時無需提交裝置列表。效率上面大大增加了。

不過epool

 

iocp使用了另外一種完全不同的通知方式。

通過iocp訪問裝置時,直接提交命令+使用者緩衝區至核心驅動,然後立即返回。此時使用者線程可以繼續運行做其他的事情。

使用者想知道命令完成狀態時,通過GetQueuedCompletionStatus來擷取完成的請求和資料。

所以使用者可以先提交一大堆io請求,然後慢慢處理返回。

 

iocp比epoll高效的原因在於,epool每次查詢後,返回的還是一個狀態數組,很可能大部分資料是沒用的,但是應用程式還是的迴圈來找有用的狀態。

而iocp每次返回的東西必定是有用的,使用者可以直接使用結果,減少了尋找這一步,所以對於大量裝置時效率比epool高

 

其他io通知方式:

kqueue 因為沒有用過,不敢妄加評論。

 

----------------------------------------------------------------------------------------------------------------------------------------------

關於iocp在socket伺服器上應用

 

首先,iocp這東西跟線程沒什麼關係,他只和裝置打交道。

所謂的iocp work thread說法本來就是有問題的,應該是thread worked with iocp, 也就是io+邏輯線程。

NumberOfConcurrentThreads 是指最多有多少個線程可以同時查詢這個iocp的狀態,多餘此數位線程查詢時全部掛起在GetQueuedCompletionStatus。

 

其次,io速度和處理速度的平衡性。

假設一個伺服器程式,每秒能接受 1m位元組的資料請求,但是其cpu上運算過程只能處理512k的資料運算,

那麼單純的提高io效率最終結果就是buffer溢出,伺服器崩潰。

 

現在來看看iocp socket在伺服器上的的2種使用方法。

A. 1個iocp對象,NumberOfConcurrentThreads =nCpu*2, threads workd with iocp=nCpu*2。

msdn裡面iocp推薦的做法,code project上的實現也是這種。

這種模式本身是沒有什麼問題的,但是因為誤解和錯誤的用法,會引起效率損失。

 

msdn裡面例子上面,iocp是對檔案操作,這和socket操作有細微的不同。

比如做檔案密碼編譯演算法。檔案可以劃分為等大小的小塊,然後提交讀取請求,擷取資料後計算,提交寫請求。這些都在 threads workd with iocp裡面完成。

因為其資料沒有前後相關性,thread可以完全的並發處理,此時最為高效。而且自動的平衡了io和處理速度。

同時在io速度低時,被喚醒的thread 可能永遠是第一個(iocp內部跟蹤了線程標誌,知道哪個是最近被喚醒的線程)。線程切換開銷最小。

此為理想狀態下的伺服器情況。

而現實是殘酷的,對於socket操作來說,因為收到資料有很大程度上的前後相關性,可能2個線程分別擷取了同一個串連的2段讀取資料,這樣的話,後一個線程只能等待前面一個線程完成操作,然後鎖定串連再做處理。

同時,每次到達的資料也可能不會是一個完整的可以處理資料區塊,此時還的另外發起讀請求。直到擷取到完整資料區段拼包處理。

此類模型代碼複雜,如果應用方式正確的話是個不錯的選擇。

 

 

錯誤的使用方法:

某些應用方法是把這整個模型作為一個io線程來用,然後單獨的一個或幾個邏輯線程處理輸入緩衝,那麼造成的問題是 邏輯處理時間為0,io效率最大化。 假設邏輯線程跟不上io速度的話,server 最終就是crash。(緩衝區一直增加)

 

B. n個iocp對象,n個線程,socket平均綁定至各iocp對象

n=1時,就是select/pool/epool的工作方式了。單線程,不會有包的前後順序問題,效率受邏輯處理速度影響。

n>1時,密集io情況下,少做了包順序檢查看起來比A方式效率要高些,但是如果鎖定操作比較多的話,反倒降低效率。只有一個線程,等待的話其他io也沒法處理了。

同時在請求較少時,線程切換開銷很大。

 

此模型最好的用法是作為單獨的io線程,只使用1個線程做專門的io, 其他線程做邏輯處理資料。

此時io線程邏輯處理時間基本沒有,io效率得以最大化。

 

另外對於簡單的如echo/socks這類協議的做server也很實用。

 

現在總結下2種方式:

A.n個(iocp+邏輯線程)

B.iocp io線程1個 +      n個邏輯線程

 

測試:

測試環境

server和client一樣: win7 64 +8g ,100m網路。cpu i5 2500k

server端是一個簡單的登陸+echo協議。登陸請求成功後echo用戶端的請求內容。

client端登陸成功後,每100ms發送20byte請求內容,其中4byte是自訂協議的包頭。

2邊都使用了tcp_nodely 1,用來類比大量的ip包。

5k個串連。

初步估計單向流量為(20+20)*10*串連數。=2MB  16mbits

實際流量為56mbit

server端:

A方式實現,4線程                  cpu消耗在20%左右,其中約3/4為kernel時間                                                       

B方式單線程實現(io和邏輯一起一個線程)   與上面差不多,看起來cpu消耗似乎低點,應該是跟線程切換有關

 

也就是說,如果用單獨的io線程來使用iocp,1個線程就是最優解,其他方法只會降低效率。

對於io線程的讀請求,應該由邏輯線程式控制制,io線程只控制需要組包時的讀請求。 避免緩衝溢出。

寫請求可以由io線程維護隊列(或者只計數)避免緩衝區溢位。

 

補充些iocp容易犯的錯誤:

1.acceptex 

acceptEx不要傳遞額外的讀取buffer(recvdatalength=0),避免拒絕服務的攻擊

(惡意用戶端串連上不發送資料,此時io未完成,無法投遞新accpetEx,耗完後其他用戶端無法串連)

2.send/recv/read/write直接返回成功的時候,不要回收緩衝區contex,成功的調用iocp會有通知的。

只有在io調用失敗時才需要自己回收,其他的都在iocp result裡面處理。

3.overlapped和wsbuf有些說法是不要放在一起,會出現nobuf的error,經測試,這種說法沒有道理。

wsae_nobuf是因為太多掛起的io調用,把系統記憶體耗完了,跟記憶體鎖定沒關係。

唯一可能最佳化就是記憶體頁對齊的地方。

 

 

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.