IO的阻塞兼丰、非阻塞,同步唆缴、異步鳍征,BIO,NIO面徽,AIO

參考來(lái)自:也談I/O模型

事先說(shuō)明:

IO有內(nèi)存IO艳丛、網(wǎng)絡(luò)IO、磁盤(pán)IO三種趟紊,通常我們說(shuō)的是后兩者氮双,所以spring webflux適用于有大量網(wǎng)絡(luò)I/O或者磁盤(pán)I/O的情況,例如需要從數(shù)據(jù)庫(kù)加載大量數(shù)據(jù)(數(shù)據(jù)庫(kù)可能存在于其他機(jī)器上霎匈,需要通過(guò)網(wǎng)絡(luò)加載數(shù)據(jù))戴差,從本地加載音視頻文件(磁盤(pán)I/O)

阻塞和非阻塞,是函數(shù)/方法的實(shí)現(xiàn)方式铛嘱,即在數(shù)據(jù)就緒之前是立刻返回還是等待暖释。

以文件IO為例,一個(gè)IO讀過(guò)程是文件數(shù)據(jù)從磁盤(pán)→內(nèi)核緩沖區(qū)→用戶(hù)內(nèi)存的過(guò)程。同步與異步的區(qū)別主要在于數(shù)據(jù)從內(nèi)核緩沖區(qū)→用戶(hù)內(nèi)存這個(gè)過(guò)程需不需要用戶(hù)進(jìn)程等待墨吓。(網(wǎng)絡(luò)IO把磁盤(pán)換做網(wǎng)卡即可)

IO模型

同步阻塞

去餐館吃飯球匕,點(diǎn)一個(gè)自己最?lèi)?ài)吃的蓋澆飯,然后在原地等著一直到蓋澆飯做好帖烘,自己端到餐桌就餐亮曹。這就是典型的同步阻塞。當(dāng)廚師給你做飯的時(shí)候秘症,你需要一直在那里等著照卦。

網(wǎng)絡(luò)編程中,讀取客戶(hù)端的數(shù)據(jù)需要調(diào)用recvfrom历极。在默認(rèn)情況下窄瘟,這個(gè)調(diào)用會(huì)一直阻塞直到數(shù)據(jù)接收完畢,就是一個(gè)同步阻塞的IO方式趟卸。這也是最簡(jiǎn)單的IO模型蹄葱,在通常fd較少、就緒很快的情況下使用是沒(méi)有問(wèn)題的锄列。

同步非阻塞

接著上面的例子扎筒,你每次點(diǎn)完飯就在那里等著荣刑,突然有一天你發(fā)現(xiàn)自己真傻。于是,你點(diǎn)完之后仅乓,就回桌子那里坐著,然后估計(jì)差不多了,就問(wèn)老板飯好了沒(méi),如果好了就去端情萤,沒(méi)好的話就等一會(huì)再去問(wèn),依次循環(huán)直到飯做好摹恨。這就是同步非阻塞筋岛。

這種方式在編程中對(duì)socket設(shè)置O_NONBLOCK即可。但此方式僅僅針對(duì)網(wǎng)絡(luò)IO有效晒哄,對(duì)磁盤(pán)IO并沒(méi)有作用睁宰。因?yàn)楸镜匚募蘒O就沒(méi)有被認(rèn)為是阻塞,我們所說(shuō)的網(wǎng)絡(luò)IO的阻塞是因?yàn)榫W(wǎng)路IO有無(wú)限阻塞的可能寝凌,而本地文件除非是被鎖住柒傻,否則是不可能無(wú)限阻塞的,因此只有鎖這種情況下较木,O_NONBLOCK才會(huì)有作用红符。而且,磁盤(pán)IO時(shí)要么數(shù)據(jù)在內(nèi)核緩沖區(qū)中直接可以返回劫映,要么需要調(diào)用物理設(shè)備去讀取违孝,這時(shí)候進(jìn)程的其他工作都需要等待。因此泳赋,后續(xù)的IO復(fù)用和信號(hào)驅(qū)動(dòng)IO對(duì)文件IO也是沒(méi)有意義的雌桑。

此外,需要說(shuō)明的一點(diǎn)是nginx和node中對(duì)于本地文件的IO是用線程的方式模擬非阻塞的效果的祖今,而對(duì)于靜態(tài)文件的io校坑,使用zero copy(例如sendfile)的效率是非常高的。

IO復(fù)用

接著上面的列子千诬,你點(diǎn)一份飯然后循環(huán)的去問(wèn)好沒(méi)好顯然有點(diǎn)得不償失耍目,還不如就等在那里直到準(zhǔn)備好,但是當(dāng)你點(diǎn)了好幾樣飯菜的時(shí)候徐绑,你每次都去問(wèn)一下所有飯菜的狀態(tài)(未做好/已做好)肯定比你每次阻塞在那里等著好多了邪驮。當(dāng)然,你問(wèn)的時(shí)候是需要阻塞的傲茄,一直到有準(zhǔn)備好的飯菜或者你等的不耐煩(超時(shí))毅访。這就引出了IO復(fù)用,也叫多路IO就緒通知盘榨。這是一種進(jìn)程預(yù)先告知內(nèi)核的能力喻粹,讓內(nèi)核發(fā)現(xiàn)進(jìn)程指定的一個(gè)或多個(gè)IO條件就緒了,就通知進(jìn)程草巡。使得一個(gè)進(jìn)程能在一連串的事件上等待守呜。

IO復(fù)用的實(shí)現(xiàn)方式目前主要有select、poll和epoll。

select和poll的原理基本相同:

注冊(cè)待偵聽(tīng)的fd(這里的fd創(chuàng)建時(shí)最好使用非阻塞)

每次調(diào)用都去檢查這些fd的狀態(tài)查乒,當(dāng)有一個(gè)或者多個(gè)fd就緒的時(shí)候返回

返回結(jié)果中包括已就緒和未就緒的fd

相比select弥喉,poll解決了單個(gè)進(jìn)程能夠打開(kāi)的文件描述符數(shù)量有限制這個(gè)問(wèn)題:select受限于FD_SIZE的限制,如果修改則需要修改這個(gè)宏重新編譯內(nèi)核侣颂;而poll通過(guò)一個(gè)pollfd數(shù)組向內(nèi)核傳遞需要關(guān)注的事件档桃,避開(kāi)了文件描述符數(shù)量限制。

此外憔晒,select和poll共同具有的一個(gè)很大的缺點(diǎn)就是包含大量fd的數(shù)組被整體復(fù)制于用戶(hù)態(tài)和內(nèi)核態(tài)地址空間之間,開(kāi)銷(xiāo)會(huì)隨著fd數(shù)量增多而線性增大蔑舞。

select和poll就類(lèi)似于上面說(shuō)的就餐方式拒担。但當(dāng)你每次都去詢(xún)問(wèn)時(shí),老板會(huì)把所有你點(diǎn)的飯菜都輪詢(xún)一遍再告訴你情況攻询,當(dāng)大量飯菜很長(zhǎng)時(shí)間都不能準(zhǔn)備好的情況下是很低效的从撼。于是,老板有些不耐煩了钧栖,就讓廚師每做好一個(gè)菜就通知他低零。這樣每次你再去問(wèn)的時(shí)候,他會(huì)直接把已經(jīng)準(zhǔn)備好的菜告訴你拯杠,你再去端掏婶。這就是事件驅(qū)動(dòng)IO就緒通知的方式-epoll。

epoll的出現(xiàn)潭陪,解決了select雄妥、poll的缺點(diǎn):

基于事件驅(qū)動(dòng)的方式,避免了每次都要把所有fd都掃描一遍依溯。

epoll_wait只返回就緒的fd老厌。

epoll使用nmap內(nèi)存映射技術(shù)避免了內(nèi)存復(fù)制的開(kāi)銷(xiāo)。

epoll的fd數(shù)量上限是操作系統(tǒng)的最大文件句柄數(shù)目,這個(gè)數(shù)目一般和內(nèi)存有關(guān)黎炉,通常遠(yuǎn)大于1024枝秤。

目前,epoll是Linux2.6下最高效的IO復(fù)用方式慷嗜,也是Nginx淀弹、Node的IO實(shí)現(xiàn)方式。而在freeBSD下洪添,kqueue是另一種類(lèi)似于epoll的IO復(fù)用方式垦页。

此外,對(duì)于IO復(fù)用還有一個(gè)水平觸發(fā)和邊緣觸發(fā)的概念:

水平觸發(fā):當(dāng)就緒的fd未被用戶(hù)進(jìn)程處理后干奢,下一次查詢(xún)依舊會(huì)返回痊焊,這是select和poll的觸發(fā)方式。

邊緣觸發(fā):無(wú)論就緒的fd是否被處理,下一次不再返回薄啥。理論上性能更高辕羽,但是實(shí)現(xiàn)相當(dāng)復(fù)雜,并且任何意外的丟失事件都會(huì)造成請(qǐng)求處理錯(cuò)誤垄惧。epoll默認(rèn)使用水平觸發(fā)刁愿,通過(guò)相應(yīng)選項(xiàng)可以使用邊緣觸發(fā)。

信號(hào)驅(qū)動(dòng)

上文的就餐方式還是需要你每次都去問(wèn)一下飯菜狀況到逊。于是铣口,你再次不耐煩了,就跟老板說(shuō)觉壶,哪個(gè)飯菜好了就通知我一聲吧脑题。然后就自己坐在桌子那里干自己的事情。更甚者铜靶,你可以把手機(jī)號(hào)留給老板叔遂,自己出門(mén),等飯菜好了直接發(fā)條短信給你争剿。這就類(lèi)似信號(hào)驅(qū)動(dòng)的IO模型已艰。

流程如下:

開(kāi)啟套接字信號(hào)驅(qū)動(dòng)IO功能

系統(tǒng)調(diào)用sigaction執(zhí)行信號(hào)處理函數(shù)(非阻塞,立刻返回)

數(shù)據(jù)就緒蚕苇,生成sigio信號(hào)哩掺,通過(guò)信號(hào)回調(diào)通知應(yīng)用來(lái)讀取數(shù)據(jù)。

異步非阻塞

之前的就餐方式捆蜀,到最后總是需要你自己去把飯菜端到餐桌疮丛。這下你也不耐煩了,于是就告訴老板辆它,能不能飯好了直接端到你的面前或者送到你的家里(外賣(mài))誊薄。這就是異步非阻塞IO了。

對(duì)比信號(hào)驅(qū)動(dòng)IO锰茉,異步IO的主要區(qū)別在于:信號(hào)驅(qū)動(dòng)由內(nèi)核告訴我們何時(shí)可以開(kāi)始一個(gè)IO操作(數(shù)據(jù)在內(nèi)核緩沖區(qū)中)呢蔫,而異步IO則由內(nèi)核通知IO操作何時(shí)已經(jīng)完成(數(shù)據(jù)已經(jīng)在用戶(hù)空間中)。

異步IO又叫做事件驅(qū)動(dòng)IO飒筑,在Unix中片吊,POSIX1003.1標(biāo)準(zhǔn)為異步方式訪問(wèn)文件定義了一套庫(kù)函數(shù),定義了AIO的一系列接口协屡。使用aio_read或者aio_write發(fā)起異步IO操作俏脊。使用aio_error檢查正在運(yùn)行的IO操作的狀態(tài)。

網(wǎng)絡(luò)編程模型

上文講述了UNIX環(huán)境的五種IO模型肤晓∫叮基于這五種模型认然,在Java中,隨著NIO和NIO2.0(AIO)的引入漫萄,一般具有以下幾種網(wǎng)絡(luò)編程模型:

BIO

NIO

AIO

BIO

BIO是一個(gè)典型的網(wǎng)絡(luò)編程模型卷员,是通常我們實(shí)現(xiàn)一個(gè)服務(wù)端程序的過(guò)程,步驟如下:

主線程accept請(qǐng)求阻塞

請(qǐng)求到達(dá)腾务,創(chuàng)建新的線程來(lái)處理這個(gè)套接字毕骡,完成對(duì)客戶(hù)端的響應(yīng)。

主線程繼續(xù)accept下一個(gè)請(qǐng)求

這種模型有一個(gè)很大的問(wèn)題是:當(dāng)客戶(hù)端連接增多時(shí)岩瘦,服務(wù)端創(chuàng)建的線程也會(huì)暴漲未巫,系統(tǒng)性能會(huì)急劇下降。因此启昧,在此模型的基礎(chǔ)上橱赠,類(lèi)似于 tomcat的bio connector,采用的是線程池來(lái)避免對(duì)于每一個(gè)客戶(hù)端都創(chuàng)建一個(gè)線程箫津。有些地方把這種方式叫做偽異步IO(把請(qǐng)求拋到線程池中異步等待處理)。

NIO

JDK1.4開(kāi)始引入了NIO類(lèi)庫(kù)宰啦,這里的NIO指的是Non-blcok IO苏遥,主要是使用Selector多路復(fù)用器來(lái)實(shí)現(xiàn)。Selector在Linux等主流操作系統(tǒng)上是通過(guò)epoll實(shí)現(xiàn)的赡模。

NIO的實(shí)現(xiàn)流程田炭,類(lèi)似于select:

創(chuàng)建ServerSocketChannel監(jiān)聽(tīng)客戶(hù)端連接并綁定監(jiān)聽(tīng)端口,設(shè)置為非阻塞模式漓柑。

創(chuàng)建Reactor線程教硫,創(chuàng)建多路復(fù)用器(Selector)并啟動(dòng)線程。

將ServerSocketChannel注冊(cè)到Reactor線程的Selector上辆布。監(jiān)聽(tīng)accept事件瞬矩。

Selector在線程run方法中無(wú)線循環(huán)輪詢(xún)準(zhǔn)備就緒的Key。

Selector監(jiān)聽(tīng)到新的客戶(hù)端接入锋玲,處理新的請(qǐng)求景用,完成tcp三次握手,建立物理連接惭蹂。

將新的客戶(hù)端連接注冊(cè)到Selector上伞插,監(jiān)聽(tīng)讀操作。讀取客戶(hù)端發(fā)送的網(wǎng)絡(luò)消息盾碗。

客戶(hù)端發(fā)送的數(shù)據(jù)就緒則讀取客戶(hù)端請(qǐng)求媚污,進(jìn)行處理。

相比BIO廷雅,NIO的編程非常復(fù)雜耗美。

AIO

JDK1.7引入NIO2.0京髓,提供了異步文件通道和異步套接字通道的實(shí)現(xiàn),是真正的異步非阻塞IO, 對(duì)應(yīng)于Unix中的異步IO幽歼。

創(chuàng)建AsynchronousServerSocketChannel朵锣,綁定監(jiān)聽(tīng)端口

調(diào)用AsynchronousServerSocketChannel的accpet方法,傳入自己實(shí)現(xiàn)的CompletionHandler甸私。包括上一步诚些,都是非阻塞的

連接傳入,回調(diào)CompletionHandler的completed方法皇型,在里面诬烹,調(diào)用AsynchronousSocketChannel的read方法,傳入負(fù)責(zé)處理數(shù)據(jù)的CompletionHandler弃鸦。

數(shù)據(jù)就緒绞吁,觸發(fā)負(fù)責(zé)處理數(shù)據(jù)的CompletionHandler的completed方法。繼續(xù)做下一步處理即可唬格。

寫(xiě)入操作類(lèi)似家破,也需要傳入CompletionHandler。

其編程模型相比NIO有了不少的簡(jiǎn)化购岗。

對(duì)比


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末汰聋,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子喊积,更是在濱河造成了極大的恐慌烹困,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,743評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件乾吻,死亡現(xiàn)場(chǎng)離奇詭異髓梅,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)绎签,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)枯饿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人辜御,你說(shuō)我怎么就攤上這事鸭你。” “怎么了擒权?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,285評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵袱巨,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我碳抄,道長(zhǎng)愉老,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,485評(píng)論 1 283
  • 正文 為了忘掉前任剖效,我火速辦了婚禮嫉入,結(jié)果婚禮上焰盗,老公的妹妹穿的比我還像新娘。我一直安慰自己咒林,他們只是感情好熬拒,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著垫竞,像睡著了一般澎粟。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上欢瞪,一...
    開(kāi)封第一講書(shū)人閱讀 49,821評(píng)論 1 290
  • 那天活烙,我揣著相機(jī)與錄音,去河邊找鬼遣鼓。 笑死啸盏,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的骑祟。 我是一名探鬼主播回懦,決...
    沈念sama閱讀 38,960評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼次企!你這毒婦竟也來(lái)了粉怕?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,719評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤抒巢,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后秉犹,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體蛉谜,經(jīng)...
    沈念sama閱讀 44,186評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評(píng)論 2 327
  • 正文 我和宋清朗相戀三年崇堵,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了型诚。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,650評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡鸳劳,死狀恐怖狰贯,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情赏廓,我是刑警寧澤涵紊,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站幔摸,受9級(jí)特大地震影響摸柄,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜既忆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評(píng)論 3 313
  • 文/蒙蒙 一驱负、第九天 我趴在偏房一處隱蔽的房頂上張望嗦玖。 院中可真熱鬧,春花似錦跃脊、人聲如沸宇挫。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,757評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)器瘪。三九已至,卻和暖如春拼缝,著一層夾襖步出監(jiān)牢的瞬間娱局,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,991評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工咧七, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留衰齐,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,370評(píng)論 2 360
  • 正文 我出身青樓继阻,卻偏偏與公主長(zhǎng)得像耻涛,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子瘟檩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評(píng)論 2 349

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