[轉(zhuǎn)]IO理解:阻塞、非阻塞、同步、異步

相關(guān)概念:

在討論IO的時(shí)候,參與者通常有兩個(gè)角色:系統(tǒng)內(nèi)核和用戶進(jìn)程。用戶進(jìn)程發(fā)送 IO請(qǐng)求過(guò)后,系統(tǒng)內(nèi)核在準(zhǔn)備好IO數(shù)據(jù)后,會(huì)通過(guò)內(nèi)存拷貝的方式,將準(zhǔn)備好的緩存IO數(shù)據(jù)共享給用戶進(jìn)程緩存。

Java IO: 同步阻塞IO

調(diào)用InputStream.read()或者OutputStream.write()時(shí),用戶進(jìn)程會(huì)阻塞住直到數(shù)據(jù)就緒,相當(dāng)于一線程一連接的方式。所以在采用Java IO時(shí),在Server端通常會(huì)采用對(duì)于每個(gè)新連接,起一個(gè)新的線程去處理,這樣后來(lái)的連接就不用等到之前的完成才能操作。但也帶來(lái)了問(wèn)題,畢竟線程是系統(tǒng)的稀缺資源,數(shù)量上會(huì)有瓶頸,達(dá)到一定數(shù)量后,性能急劇下降,內(nèi)存崩潰。不能應(yīng)對(duì)大量連接的情況,而且線程切換很耗費(fèi)系統(tǒng)資源。

Java NIO:同步非阻塞IO

基于Java IO的缺點(diǎn),NIO采用了新的設(shè)計(jì)方式,核心在Channel,Buffer,Selector。非阻塞主要依靠Selector,Channel在Selector上注冊(cè)自己感興趣的事件,然后Selector線程會(huì)輪詢注冊(cè)在自己身上的Channel,當(dāng)有數(shù)據(jù)準(zhǔn)備就緒時(shí),就通知相應(yīng)的Channel。這樣一個(gè)Selector可以管理多個(gè)Channel,但實(shí)際上還是阻塞的,現(xiàn)在不阻塞IO層面了,阻塞在Selector線程上了。而且采用輪詢的方式,效率比較低。

Java AIO:異步非阻塞IO

在Java NIO的基礎(chǔ)上,增加了AsynchronousChannelGroup,CompletionHandler,其中AsynchronousChannelGroup起到了事件收集和任務(wù)分發(fā)的作用,而CompletionHandler是綁定在事件上回調(diào)機(jī)制,從而達(dá)到異步。能否真正實(shí)現(xiàn)異步,關(guān)鍵還要看系統(tǒng)底層的實(shí)現(xiàn),當(dāng)前來(lái)看只有window的iocp實(shí)現(xiàn)了真正的異步,linux上還是通過(guò)epoll來(lái)模擬,是一種偽異步。

是否異步主要在系統(tǒng)內(nèi)核數(shù)據(jù)拷貝到用戶進(jìn)程這個(gè)步驟來(lái)區(qū)分,同步的話是通知用戶進(jìn)程數(shù)據(jù)準(zhǔn)備好了,可以拷貝了,然后用戶進(jìn)程阻塞去拷貝數(shù)據(jù)。異步的話,是操作系統(tǒng)幫你把數(shù)據(jù)拷貝后,然后通知你數(shù)據(jù)好了,可以直接用了。

Select/Poll, epoll/kqueue, iocp

select模型

1. 最大并發(fā)數(shù)限制,因?yàn)橐粋€(gè)進(jìn)程所打開(kāi)的FD(文件描述符)是有限制的,由FD_SETSIZE設(shè)置,默認(rèn)值是1024/2048,因此Select模型的最大并發(fā)數(shù)就被相應(yīng)限制了。自己改改這個(gè)FD_SETSIZE?想法雖好,可是先看看下面吧…

2. 效率問(wèn)題,select每次調(diào)用都會(huì)線性掃描全部的FD集合,這樣效率就會(huì)呈現(xiàn)線性下降,把FD_SETSIZE改大的后果就是,大家都慢慢來(lái),什么?都超時(shí)了??!!

3. 內(nèi)核/用戶空間 內(nèi)存拷貝問(wèn)題,如何讓內(nèi)核把FD消息通知給用戶空間呢?在這個(gè)問(wèn)題上select采取了內(nèi)存拷貝方法。

poll模型

基本上效率和select是相同的,select缺點(diǎn)的2和3它都沒(méi)有改掉。

Epoll的提升

把其他模型逐個(gè)批判了一下,再來(lái)看看Epoll的改進(jìn)之處吧,其實(shí)把select的缺點(diǎn)反過(guò)來(lái)那就是Epoll的優(yōu)點(diǎn)了。

1. Epoll沒(méi)有最大并發(fā)連接的限制,上限是最大可以打開(kāi)文件的數(shù)目,這個(gè)數(shù)字一般遠(yuǎn)大于2048, 一般來(lái)說(shuō)這個(gè)數(shù)目和系統(tǒng)內(nèi)存關(guān)系很大,具體數(shù)目可以cat /proc/sys/fs/file-max察看。

2. 效率提升,Epoll最大的優(yōu)點(diǎn)就在于它只管你“活躍”的連接,而跟連接總數(shù)無(wú)關(guān),因此在實(shí)際的網(wǎng)絡(luò)環(huán)境中,Epoll的效率就會(huì)遠(yuǎn)遠(yuǎn)高于select和poll。

3. 內(nèi)存拷貝,Epoll在這點(diǎn)上使用了“共享內(nèi)存”,這個(gè)內(nèi)存拷貝也省略了

常用IO模型交互圖

blocking I/O:阻塞套接字


首先application調(diào)用 recvfrom()轉(zhuǎn)入kernel,注意kernel有2個(gè)過(guò)程,wait for data和copy data from kernel to user。直到最后copy complete后,recvfrom()才返回。此過(guò)程一直是阻塞的

nonblocking I/O:非阻塞套接字


可以看見(jiàn),如果直接操作它,那就是個(gè)輪詢。。直到內(nèi)核緩沖區(qū)有數(shù)據(jù)。

I/O multiplexing (select and poll)

select先阻塞,有活動(dòng)套接字才返回。與blocking I/O相比,select會(huì)有兩次系統(tǒng)調(diào)用,但是select能處理多個(gè)套接字。

signal driven I/O (SIGIO) :只有Unix系統(tǒng)支持


與I/O multiplexing (select and poll)相比,它的優(yōu)勢(shì)是,免去了select的阻塞與輪詢,當(dāng)有活躍套接字時(shí),由注冊(cè)的handler處理。

asynchronous I/O (the POSIX aio_functions)


很少有*nix系統(tǒng)支持,windows的IOCP則是此模型

完全異步的I/O復(fù)用機(jī)制,因?yàn)榭v觀上面其它四種模型,至少都會(huì)在由kernel copy data to appliction時(shí)阻塞。而該模型是當(dāng)copy完成后才通知application,可見(jiàn)是純異步的。好像只有windows的完成端口是這個(gè)模型,效率也很出色。

服務(wù)器程序策略

服務(wù)器程序策略主要指的是網(wǎng)絡(luò)編程時(shí)的開(kāi)發(fā)策略,記得原來(lái)畢業(yè)找工作面試后端開(kāi)發(fā)職位的時(shí)候這是一定會(huì)被問(wèn)到。在討論這個(gè)問(wèn)題之前,必須要說(shuō)的就是著名的C10K問(wèn)題。

C10k 問(wèn)題

C10K是current 10k connection的簡(jiǎn)寫(xiě),描述的是服務(wù)端如何處理同時(shí)到來(lái)的上萬(wàn)個(gè)client連接的問(wèn)題,簡(jiǎn)而言之就是高并發(fā)的問(wèn)題。為了解決這個(gè)問(wèn)題,有以下幾種經(jīng)典的服務(wù)端策略:

1. 為每個(gè)連接分配一個(gè)線程/進(jìn)程來(lái)處理

這個(gè)策略是指服務(wù)器為每一個(gè)到來(lái)的連接都分配一個(gè)新的線程/進(jìn)程,使用阻塞式的I/O來(lái)處理。Java和Apache都是這種策略,這種策略簡(jiǎn)單并且,能實(shí)現(xiàn)比較復(fù)雜的交互。然后系統(tǒng)分配進(jìn)程/線程是需要資源的,而且因?yàn)槭褂昧俗枞降腎/O,當(dāng)有大量連接到來(lái)時(shí)候系統(tǒng)資源會(huì)是性能瓶頸。

這個(gè)思路是最直觀,最容易想到的,以至于造成最大的誤解:

當(dāng)我們聽(tīng)說(shuō)Node.js是單線程模型的時(shí)候就認(rèn)定了他和開(kāi)發(fā)服務(wù)器程序是無(wú)緣的。

2. 一個(gè)線程同時(shí)處理多個(gè)連接

select/poll/epoll

第一種策略在高并發(fā)時(shí)會(huì)創(chuàng)建過(guò)多的線程,然而這些線程大部分時(shí)間都是在block等待數(shù)據(jù)。這種策略只用一個(gè)線程來(lái)監(jiān)聽(tīng)多個(gè)socket連接,如果有數(shù)據(jù)到來(lái)就處理,讀寫(xiě)完成后再次進(jìn)入block監(jiān)聽(tīng)。這種策略使用select/poll/epoll這樣的多路復(fù)用I/O來(lái)實(shí)現(xiàn)。

非阻塞的I/O及事件通知

同樣是只用一個(gè)線程來(lái)處理所有的客戶端連接,使用非阻塞I/O和事件機(jī)制來(lái)通知。Node.js就是使用了這種策略,這種策略實(shí)現(xiàn)簡(jiǎn)單,方便移植。缺點(diǎn)是只有一個(gè)線程所以不能充分利用CPU的多核性能,這也是Node.js用來(lái)開(kāi)發(fā)服務(wù)器程序被吐槽最多的地方。

3. 分配多個(gè)線程,每個(gè)線程處理一組連接

這是對(duì)第二種策略的改進(jìn)型,分配多個(gè)線程,每個(gè)線程負(fù)責(zé)一定量的連接請(qǐng)求,使用非阻塞的I/O和事件機(jī)制。

4. 分配多個(gè)線程,使用異步I/O

與第三種策略相比,這種策略使用異步的I/O,由內(nèi)核來(lái)完成數(shù)據(jù)的準(zhǔn)備和copy。這種策略在支持異步I/O的操作系統(tǒng)上效率很高,用戶無(wú)需任何操作就能完成數(shù)據(jù)的收發(fā),這一切都由內(nèi)核默默的完成。

舉個(gè)栗子

同樣的用上面那個(gè)小郵局取快遞的例子再來(lái)說(shuō)明下以上這四種網(wǎng)絡(luò)編程策略:

每個(gè)線程同步阻塞處理一個(gè)連接:每個(gè)碼農(nóng)都在小郵局等自己的快遞到來(lái),快遞到了之后自己把包裹取走;這種做法比較低效,大家都在等快遞,都沒(méi)時(shí)間搬磚了;

一個(gè)線程處理所有連接:讓碼農(nóng)小明一個(gè)人在小郵局等所有的快遞,只要有包裹來(lái)了就幫忙取走送到相應(yīng)人座位上;這種方式只需要小明一個(gè)人,但是如果小明送包裹的速度不夠快就會(huì)導(dǎo)致新來(lái)的包裹積壓,不能及時(shí)送達(dá);(這種需要很長(zhǎng)時(shí)間才能送達(dá)的包裹就是CPU密集型的task)

多個(gè)線程,每個(gè)線程負(fù)責(zé)一組連接:這種方式就是多找一個(gè)小明一起來(lái)完成,是第二種方式的改進(jìn)型;

多個(gè)線程,每個(gè)線程使用異步I/O負(fù)責(zé)一組連接:這種方式不用小明了,而是讓小郵局的工作人員來(lái)做之前小明的工作,這樣每個(gè)碼農(nóng)都能安心搬磚了,最大化了工作效率;


epoll也是IO復(fù)用,比傳統(tǒng)的Select/poll有很大的優(yōu)化,Linux2.6開(kāi)始都使用了epoll,而且并不是信號(hào)驅(qū)動(dòng)的,只是可以被中斷信號(hào)中斷。IO復(fù)用是調(diào)用方被動(dòng)接受內(nèi)核的通知,需要阻塞在Select/poll/epoll函數(shù)上,并不是單個(gè)IO上,就像你調(diào)用一個(gè)函數(shù)等待它返回一樣,它返回的時(shí)候就是知道內(nèi)核有數(shù)據(jù)到來(lái)的時(shí)候,它會(huì)通知你有哪些你注冊(cè)過(guò)的IO上有數(shù)據(jù)了,但是數(shù)據(jù)存在內(nèi)核中,需要你自己去從內(nèi)核拷貝數(shù)據(jù)到用戶空間(同步方式),這點(diǎn)也是同步和異步的區(qū)別,異步方式的話,當(dāng)你收到通知的時(shí)候數(shù)據(jù)已經(jīng)在用戶空間了;信號(hào)驅(qū)動(dòng)方式是基于系統(tǒng)的信號(hào)機(jī)制,你要先注冊(cè)一個(gè)信號(hào)處理函數(shù)(當(dāng)你收到系統(tǒng)信號(hào)時(shí)進(jìn)行何種反應(yīng)),處理函數(shù)會(huì)在另一個(gè)線程中執(zhí)行,所以并不會(huì)阻塞當(dāng)前的進(jìn)程,當(dāng)內(nèi)核知道有數(shù)據(jù)到來(lái),就向?qū)?yīng)的進(jìn)程發(fā)送信號(hào),通知他數(shù)據(jù)來(lái)了,快來(lái)讀,進(jìn)程收到信號(hào),啟動(dòng)一個(gè)線程執(zhí)行對(duì)應(yīng)的信號(hào)處理函數(shù)。

http://www.programgo.com/article/63742464168/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,663評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,125評(píng)論 3 414
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 175,506評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 62,614評(píng)論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,402評(píng)論 6 404
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 54,934評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,021評(píng)論 3 440
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,168評(píng)論 0 287
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,690評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,596評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,784評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,288評(píng)論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,027評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,404評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,662評(píng)論 1 280
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,398評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,743評(píng)論 2 370

推薦閱讀更多精彩內(nèi)容