windows套接字IOCP模型
本文主要探討一下windows平臺上的完成端口開發(fā)及其與之相關(guān)的幾個重要的技術(shù)概念,這些概念都是與基于IOCP的開發(fā)密切相關(guān)的,對開發(fā)人員來講,又不得不給予足夠重視的幾個概念: 1) 基于IOCP實現(xiàn)的服務(wù)吞吐量 2)IOCP模式下的線程切換 3)基于IOCP實現(xiàn)的消息的亂序問題。 一、IOCP簡介提到IOCP,大家都非常熟悉,其基本的編程模式,我就不在這里展開了。在這里我主要是把IOCP中所提及的概念做一個基本性的總結(jié)。IOCP的基本架構(gòu)圖如下:
如圖所示:在IOCP中,主要有以下的參與者: --》完成端口:是一個FIFO隊列,操作系統(tǒng)的IO子系統(tǒng)在IO操作完成后,會把相應(yīng)的IO packet放入該隊列。 --》等待者線程隊列:通過調(diào)用GetQueuedCompletionStatus API,在完成端口上等待取下一個IO packet。 --》執(zhí)行者線程組:已經(jīng)從完成端口上獲得IO packet,在占用CPU進行處理。 除了以上三種類型的參與者。我們還應(yīng)該注意兩個關(guān)聯(lián)關(guān)系,即: --》IO Handle與完成端口相關(guān)聯(lián):任何期望使用IOCP的方式來處理IO請求的,必須將相應(yīng)的IO Handle與該完成端口相關(guān)聯(lián)。需要指出的時,這里的IO Handle,可以是File的Handle,或者是Socket的Handle。 --》線程與完成端口相關(guān)聯(lián):任何調(diào)用GetQueuedCompletionStatus API的線程,都將與該完成端口相關(guān)聯(lián)。在任何給定的時候,該線程只能與一個完成端口相關(guān)聯(lián),與最后一次調(diào)用的GetQueuedCompletionStatus為準。 二、高并發(fā)的服務(wù)器(基于socket)實現(xiàn)方法一般來講,實現(xiàn)基于socket的服務(wù)器,有三種實現(xiàn)的方式(thread per request的方式,我就不提了:)): 第一、線程池的方式。使用線程池來對客戶端請求進行服務(wù)。使用這種方式時,當(dāng)客戶端對服務(wù)器的連接是短連接(所謂的短連接,即:客戶端對服務(wù)器不是長時間連接)時,是可以考慮的。但是,如若客戶端對服務(wù)器的連接是長連接時,我們需要限制服務(wù)器端的最大連接數(shù)目為線程池線程的最大數(shù)目,而這應(yīng)用的設(shè)計本身來講,是不好的設(shè)計方式,scalability會存在問題。 第二、基于Select的服務(wù)器實現(xiàn)。其本質(zhì)是,使用Select(操作系統(tǒng)提供的API)來監(jiān)視連接是否可讀,可寫,或者是否出錯。相比于前一種方式,Select允許應(yīng)用使用一個線程(或者是有限幾個線程)來監(jiān)視連接的可讀寫性。當(dāng)有連接可讀可寫時,應(yīng)用可以以non-bolock的方式讀寫socket上的數(shù)據(jù)。使用Select的方式的缺點是,當(dāng)Select所監(jiān)視的連接數(shù)目在千的數(shù)量級時,性能會打折扣。這是因為操作系統(tǒng)內(nèi)核需要在內(nèi)部對這些Socket進行輪詢,以檢查其可讀寫性。另一個問題是:應(yīng)用必須在處理完所有的可讀寫socket的IO請求之后,才能再次調(diào)用Select,進行下一輪的檢查,否則會有潛在的問題。這樣,造成的結(jié)果是,對一些請求的處理會出現(xiàn)饑餓的現(xiàn)象。一般common的做法是Select結(jié)合Leader-Follower設(shè)計模式使用。不過不管怎樣,Select的本質(zhì)造成了其在Scalability的問題是不如IOCP,這也是很多high-scalabe的服務(wù)器采用IOCP的原因。 第三、IOCP實現(xiàn)高并發(fā)的服務(wù)器。IOCP是實現(xiàn)high-scalabe的服務(wù)器的首選。其特點我們專門在下一小姐陳述。 三、IOCP開發(fā)的幾個概念 第一、服務(wù)器的吞吐量問題。 ????? 我們都知道,基于IOCP的開發(fā)是異步IO的,也正是這一技術(shù)的本質(zhì),決定了IOCP所實現(xiàn)的服務(wù)器的高吞吐量。我們舉一個及其簡化的例子,來說明這一問題。在網(wǎng)絡(luò)服務(wù)器的開發(fā)過程中,影響其性能吞吐量的,有很多因素,在這里,我們只是把關(guān)注點放在兩個方面,即:網(wǎng)絡(luò)IO速度與Disk IO速度。我們假設(shè):在一個千兆的網(wǎng)絡(luò)環(huán)境下,我們的網(wǎng)絡(luò)傳輸速度的極限是大概125M/s,而Disk IO的速度是10M/s。在這樣的前提下,慢速的Disk 設(shè)備會成為我們整個應(yīng)用的瓶頸。我們假設(shè)線程A負責(zé)從網(wǎng)絡(luò)上讀取數(shù)據(jù),然后將這些數(shù)據(jù)寫入Disk。如果對Disk的寫入是同步的,那么線程A在等待寫完Disk的過程是不能再從網(wǎng)絡(luò)上接受數(shù)據(jù)的,在寫入Disk的時間內(nèi),我們可以認為這時候Server的吞吐量為0(沒有接受新的客戶端請求)。對于這樣的同步讀寫Disk,一些的解決方案是通過增加線程數(shù)來增加服務(wù)器處理的吞吐量,即:當(dāng)線程A從網(wǎng)絡(luò)上接受數(shù)據(jù)后,驅(qū)動另外單獨的線程來完成讀寫Disk任務(wù)。這樣的方案缺點是:需要線程間的合作,需要線程間的切換(這是另一個我們要討論的問題)。而IOCP的異步IO本質(zhì),就是通過操作系統(tǒng)內(nèi)核的支持,允許線程A以非阻塞的方式向IO子系統(tǒng)投遞IO請求,而后馬上從網(wǎng)絡(luò)上讀取下一個客戶端請求。這樣,結(jié)果是:在不增加線程數(shù)的情況下,IOCP大大增加了服務(wù)器的吞吐量。說到這里,聽起來感覺很像是DMA。的確,許多軟件的實現(xiàn)技術(shù),在本質(zhì)上,與硬件的實現(xiàn)技術(shù)是相通的。另外一個典型的例子是硬件的流水線技術(shù),同樣,在軟件領(lǐng)域,也有很著名的應(yīng)用。好像話題扯遠了,呵呵:) 第二、線程間的切換問題。服務(wù)器的實現(xiàn),通過引入IOCP,會大大減少Thread切換帶來的額外開銷。我們都知道,對于服務(wù)器性能的一個重要的評估指標就是:System/Context Switches,即單位時間內(nèi)線程的切換次數(shù)。如果在每秒內(nèi),線程的切換次數(shù)在千的數(shù)量級上,這就意味著你的服務(wù)器性能值得商榷。Context Switches/s應(yīng)該越小越好。說到這里,我們來重新審視一下IOCP。完成端口的線程并發(fā)量可以在創(chuàng)建該完成端口時指定(即NumberOfConcurrentThreads參數(shù))。該并發(fā)量限制了與該完成端口相關(guān)聯(lián)的可運行線程的數(shù)目(就是前面我在IOCP簡介中提到的執(zhí)行者線程組的最大數(shù)目)。當(dāng)與該完成端口相關(guān)聯(lián)的可運行線程的總數(shù)目達到了該并發(fā)量,系統(tǒng)就會阻塞任何與該完成端口相關(guān)聯(lián)的后續(xù)線程的執(zhí)行,直到與該完成端口相關(guān)聯(lián)的可運行線程數(shù)目下降到小于該并發(fā)量為止。最有效的假想是發(fā)生在有完成包在隊列中等待,而沒有等待被滿足,因為此時完成端口達到了其并發(fā)量的極限。此時,一個正在運行中的線程調(diào)用GetQueuedCompletionStatus時,它就會立刻從隊列中取走該完成包。這樣就不存在著環(huán)境的切換,因為該處于運行中的線程就會連續(xù)不斷地從隊列中取走完成包,而其他的線程就不能運行了。完成端口的線程并發(fā)量的建議值就是你系統(tǒng)CPU的數(shù)目。在這里,要區(qū)分清楚的是,完成端口的線程并發(fā)量與你為完成端口創(chuàng)建的工作者線程數(shù)是沒有任何關(guān)系的,工作者線程數(shù)的數(shù)目,完全取決于你的整個應(yīng)用的設(shè)計(當(dāng)然這個不宜過大,否則失去了IOCP的本意:))。 第三、IOCP開發(fā)過程中的消息亂序問題。使用IOCP開發(fā)的問題在于它的復(fù)雜。我們都知道,在使用TCP時,TCP協(xié)議本身保證了消息傳遞的次序性,這大大降低了上層應(yīng)用的復(fù)雜性。但是當(dāng)使用
?
三個線程同時從IOCP中讀取Msg1, Msg2,與Msg3。由于TCP本身消息傳遞的有序性,所以,在IOCP隊列內(nèi),Msg1-Msg2-Msg3保證了有序性。三個線程分別從IOCP中取出Msg1,Msg2與Msg3,然后三個線程都會將各自取到的消息投遞到邏輯層處理。在邏輯處理層的實現(xiàn),我們不應(yīng)該假定Msg1-Msg2-Msg3順序,原因其實很簡單,在Time 1~Time 2的時間段內(nèi),三個線程被操作系統(tǒng)調(diào)度的先后次序是不確定的,所以在到達邏輯處理層, Msg1,Msg2與Msg3的次序也就是不確定的。所以,邏輯處理層的實現(xiàn),必須考慮消息亂序的情況,必須考慮多線程環(huán)境下的程序?qū)崿F(xiàn)。在這里,我把消息亂序的問題單列了出來。其實在IOCP的開發(fā)過程中,相比于同步的方式,應(yīng)該還有其它更多的難題需要解決,這也是與Select方式相比,IOCP的缺點,實現(xiàn)復(fù)雜度高。 結(jié)束語: ??? ACE的Proactor Framework, 對windows平臺的IOCP做了基于Proactor設(shè)計模式的,面向?qū)ο蟮姆庋b,這在一定程度上簡化了應(yīng)用開發(fā)的難度,是一個很好的異步IO的開發(fā)框架,推薦學(xué)習(xí)使用。 Reference:Microsoft Technet,Inside I/O Completion Ports
posted on 2007-08-26 16:06 愛上龍卷風(fēng) 閱讀(2314) 評論(16) 編輯 收藏 引用Feedback
對于你提出的第三個問題,亂序問題 一般寫IOCP程序,都會讓每個socket和一個buffer關(guān)聯(lián) 只要讓W(xué)SARecv保證每次按次序的寫到buffer 然后邏輯層也按照次序的來讀取信息(需要有鎖來控制) 這樣就不會有亂序問題了。 不知道你對于這個問題有什么高招???回復(fù)??更多評論 # re: 完成端口(IOCP)編程探討 2007-09-06 10:11 NULL回答下上面的問題,你的問題就有問題,如果只有一個recv投遞,那就不會存在亂序的問題,如果你投遞n個,按順序1.2.3,但是你怎么保證recv不是按照2.3.1返回的呢???回復(fù)??更多評論 # re: 完成端口(IOCP)編程探討 2007-10-20 15:30 RedFox我也是進來討論你提出的第三個問題的。 通常的做法是一個 Socket 用一個 WSARecv 異步操作,有完成事件時,先處理完這個異步操作時,才再次發(fā)出下一個 WSARecv 異步操作。這樣就不會有你說的消息亂序問題了。 而你說的一次對同一個 socket 提交 n 個 WSARecv 操作。我認為是不可取的。 回復(fù)??更多評論 # re: 完成端口(IOCP)編程探討 2007-10-21 20:15 愛上龍卷風(fēng)@RedFox 你應(yīng)該弄錯了,的確,如果按照你的說法,就是單線程了,沒有多線程。 當(dāng)然沒有問題。但是,你的performance是沒法保證的,幾乎沒有什么 throughput的。回復(fù)??更多評論 # re: 完成端口(IOCP)編程探討 2007-10-21 20:17 愛上龍卷風(fēng)@小明 對于你的問題,我同意NULL的解釋??回復(fù)??更多評論 # re: 完成端口(IOCP)編程探討 2007-10-22 08:41 RedFox可能是我水平不夠。 的確,我是對每個 socket 做一個 WSARecv. 處理完這個 WSARecv 後我再發(fā)起下一個 WSARecv. 如果有 1000個連接,而 IOCP 裡就有 1000個 IO請求包。 而你說的一個 socket 就發(fā)起 N 個 WSARecv. 首先,讀到的數(shù)據(jù)次序不能保證,再次,如果 socket 關(guān)閉時,你也會收到 N 個關(guān)閉消息,你應(yīng)該在哪個關(guān)閉消息時去釋放資源呢???回復(fù)??更多評論 # re: 完成端口(IOCP)編程探討 2007-11-11 14:19 愛上龍卷風(fēng)@RedFox 你說的收到N 個關(guān)閉消息,指的是你自己定義的嗎? 不管怎么樣,引用計數(shù)技術(shù)應(yīng)該能幫到你。??回復(fù)??更多評論 # re: 完成端口(IOCP)編程探討 2008-01-07 17:12 唐新發(fā)錯別字:小組不是小姐 第三、IOCP實現(xiàn)高并發(fā)的服務(wù)器。IOCP是實現(xiàn)high-scalabe的服務(wù)器的首選。其特點我們專門在下一小姐陳述。??回復(fù)??更多評論 # re: 完成端口(IOCP)編程探討 2008-01-07 23:08 abettor.org@RedFox 我贊同你的看法。 但是,是否有更高效的辦法呢? 我覺得如果業(yè)務(wù)邏輯層速度很慢的化,可以把按上述方法WSARecv到的數(shù)據(jù)再放到一個池里緩沖一下。不知道會不會適得其反。??回復(fù)??更多評論 # re: 完成端口(IOCP)編程探討 2008-01-11 22:09 愛上龍卷風(fēng)@abettor.org 應(yīng)該不是業(yè)務(wù)邏輯層速度很慢的問題,cpu的指令執(zhí)行都是很快的,除非你訪問慢速的IO設(shè)備。 對于業(yè)務(wù)邏輯層,應(yīng)該提高的是并發(fā)的吞吐量。 回復(fù)??更多評論 # re: 完成端口(IOCP)編程探討 2008-03-31 09:05 RedFox@abettor.org IOCP 只處理封包,收到一個完整的包後,就POST到工作線程池,由工作線程池進行分配線程處理??回復(fù)??更多評論 # re: 完成端口(IOCP)編程探討 2008-05-24 23:46 iunknown@愛上龍卷風(fēng) 你應(yīng)該弄錯了,的確,如果按照你的說法,就是單線程了,沒有多線程。 當(dāng)然沒有問題。但是,你的performance是沒法保證的,幾乎沒有什么 throughput的。 關(guān)于這一點,有實現(xiàn)過一個用單線程負責(zé) IO 操作,線程池負責(zé)業(yè)務(wù)邏輯處理的 server framework 。負責(zé) IO 的單線程,主要是執(zhí)行 WSARecv, WSASend ,GetQueuedCompletionPort,具體的業(yè)務(wù)邏輯不在這個線程里面執(zhí)行。 用這個框架實現(xiàn)過一個 echo server ,進行大壓力測試,發(fā)現(xiàn) throughput 還是很高的。可以認為在目前的 CPU 速度下,只是執(zhí)行 WSARecv,WSASend,GetQueuedCompletionPort ,還是足夠快的。 如果在同一臺機運行 client 和 server,測試的結(jié)果是可以達到每秒 send 10萬, recv 10萬。??回復(fù)??更多評論 # re: 完成端口(IOCP)編程探討 2008-05-24 23:49 iunknown@RedFox 我也是進來討論你提出的第三個問題的。 通常的做法是一個 Socket 用一個 WSARecv 異步操作,有完成事件時,先處理完這個異步操作時,才再次發(fā)出下一個 WSARecv 異步操作。這樣就不會有你說的消息亂序問題了。 而你說的一次對同一個 socket 提交 n 個 WSARecv 操作。我認為是不可取的。 也是用這個方法來實現(xiàn)的,這樣做會比較簡單,可以避免消息的亂序問題。而且在關(guān)閉 socket 釋放資源的時候,也相對容易控制。??回復(fù)??更多評論 # re: 完成端口(IOCP)編程探討 2008-05-29 16:56 愛上龍卷風(fēng)@iunknown 關(guān)于你提到的框架,本質(zhì)上是多線線程的。負責(zé) IO 的線程數(shù)目理論上本來就應(yīng)該是你機器cpu的個數(shù)。前提是你保證你的io設(shè)計是異步執(zhí)行的。??回復(fù)??更多評論 # re: 完成端口(IOCP)編程探討 2008-06-09 17:14 iunknown@愛上龍卷風(fēng) >>>關(guān)于你提到的框架,本質(zhì)上是多線線程的。負責(zé) IO 的線程數(shù)目理論上本來就應(yīng)該是你機器cpu的個數(shù)。前提是你保證你的io設(shè)計是異步執(zhí)行的。框架中用到了多線程,不過用于處理 IOCP 的線程(或者稱為用于處理前端網(wǎng)絡(luò) IO 的線程)就只有一個。后端的線程池是用于處理具體的業(yè)務(wù)的,不會涉及前端的網(wǎng)絡(luò) IO 的。前端的網(wǎng)絡(luò) IO 可以非常清晰地剝離出來,由一個單獨的線程來負責(zé)。在 input 的時候,在前端的 IO 線程中讀入數(shù)據(jù),放入 inputqueue,線程池負責(zé) inputqueue;output 的時候,線程池把結(jié)果放入 outputqueue ,前端 IO 線程負責(zé)處理 outputqueue 。??回復(fù)??更多評論 # re: 完成端口(IOCP)編程探討 2008-06-09 17:19 iunknown這里有一個用 zero byte buffer 策略集成 iocp 到 libevent 的方案。 具體的源代碼:http://spserver.googlecode.com/files/libevent-1.4.4-iocp-3.zipvc6 的 dsw 文件在 libevent-1.4.4-iocp-3/libevent-iocp 目錄中。 這個方案就是單線程負責(zé) IO ,里面有 echo_iocp.exe 和 testiocpstress.exe ,可以試試看它的性能。方案的描述可以看看http://monkeymail.org/archives/libevent-users/2008-June/001269.html??回復(fù)??更多評論 # re: 完成端口(IOCP)編程探討[未登錄] 2007-09-05 07:15 小明總結(jié)
以上是生活随笔為你收集整理的windows套接字IOCP模型的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 了解select模型的六大注意点
- 下一篇: SSD+HDD双硬盘+MSI主板win8