I/O模型之一:Unix的五種I/O模型

基本 Linux I/O 模型的簡(jiǎn)單矩陣:


image.png

每個(gè) I/O 模型都有自己的使用模式,它們對(duì)于特定的應(yīng)用程序都有自己的優(yōu)點(diǎn)侄旬。
Unix的五種I/O模型:
1.阻塞I/O:應(yīng)用程序調(diào)用一個(gè)IO函數(shù)煌妈,導(dǎo)致應(yīng)用程序阻塞,如果數(shù)據(jù)已經(jīng)準(zhǔn)備好仇冯,從內(nèi)核拷貝到用戶空間族操,否則一直等待下去
2.非阻塞I/O:
3.I/O復(fù)用(select和poll)
4.信號(hào)驅(qū)動(dòng)I/O(SIGIO)
5.異步I/O(Posix.1的aio_系列函數(shù))
Unix的一個(gè)輸入操作一般有兩個(gè)不同的階段:
1色难、等待數(shù)據(jù)準(zhǔn)備好枷莉。
2、從內(nèi)核到進(jìn)程拷貝數(shù)據(jù)冒掌。
對(duì)于一個(gè)套接口上的輸入操作危喉,第一步一般是等待數(shù)據(jù)到達(dá)網(wǎng)絡(luò)辜限,當(dāng)分組到達(dá)時(shí)薄嫡,它被拷貝到內(nèi)核中的某個(gè)緩沖區(qū),第二步是將數(shù)據(jù)從內(nèi)核緩沖區(qū)拷貝到應(yīng)用緩沖區(qū)吩坝。

阻塞I/O:(blocking IO)

應(yīng)用程序調(diào)用一個(gè)IO函數(shù)钉寝,導(dǎo)致應(yīng)用程序阻塞闸迷,如果數(shù)據(jù)已經(jīng)準(zhǔn)備好腥沽,從內(nèi)核拷貝到用戶空間,否則一直等待下去师溅。
在linux中墓臭,默認(rèn)情況下所有的socket都是blocking,一個(gè)典型的讀操作流程大概是這樣:


image.png

當(dāng)用戶進(jìn)程調(diào)用了recvfrom這個(gè)系統(tǒng)調(diào)用,kernel就開始了IO的第一個(gè)階段:準(zhǔn)備數(shù)據(jù)(對(duì)于網(wǎng)絡(luò)IO來說榆综,很多時(shí)候數(shù)據(jù)在一開始還沒有到達(dá)鼻疮。比如判沟,還沒有收到一個(gè)完整的UDP包。這個(gè)時(shí)候kernel就要等待足夠的數(shù)據(jù)到來)吧秕。這個(gè)過程需要等待砸彬,也就是說數(shù)據(jù)被拷貝到操作系統(tǒng)內(nèi)核的緩沖區(qū)中是需要一個(gè)過程的斯入。而在用戶進(jìn)程這邊刻两,整個(gè)進(jìn)程會(huì)被阻塞(當(dāng)然,是進(jìn)程自己選擇的阻塞)滋迈。當(dāng)kernel一直等到數(shù)據(jù)準(zhǔn)備好了杀怠,它就會(huì)將數(shù)據(jù)從kernel中拷貝到用戶內(nèi)存厅克,然后kernel返回結(jié)果证舟,用戶進(jìn)程才解除block的狀態(tài)女责,重新運(yùn)行起來。

所以墙基,blocking IO的特點(diǎn)就是在IO執(zhí)行的兩個(gè)階段都被block了残制。
非阻塞I/O模型 (nonblocking IO)

我們把一個(gè)套接口設(shè)置為非阻塞就是告訴內(nèi)核掖疮,當(dāng)所請(qǐng)求的I/O操作無法完成時(shí)浊闪,不要將進(jìn)程睡眠搁宾,而是返回一個(gè)錯(cuò)誤盖腿。這樣我們的I/O操作函數(shù)將不斷的測(cè)試數(shù)據(jù)是否已經(jīng)準(zhǔn)備好,如果沒有準(zhǔn)備好堕伪,繼續(xù)測(cè)試欠雌,直到數(shù)據(jù)準(zhǔn)備好為止疙筹。在這個(gè)不斷測(cè)試的過程中而咆,會(huì)大量的占用CPU的時(shí)間暴备。
當(dāng)一個(gè)應(yīng)用程序像這樣對(duì)一個(gè)非阻塞描述符循環(huán)調(diào)用recvfrom時(shí),我們稱之為輪循(polling)浅妆。應(yīng)用進(jìn)程連續(xù)不斷地查詢內(nèi)核凌外,看看某操作是否準(zhǔn)備好,這對(duì)CPU時(shí)間是極大的浪費(fèi)摄欲,但這種模型只是偶爾才遇到胸墙,一般是在只專門提供某種功能的系統(tǒng)中才有劳秋。


image.png

當(dāng)用戶進(jìn)程發(fā)出read操作時(shí)玻淑,如果kernel中的數(shù)據(jù)還沒有準(zhǔn)備好补履,那么它并不會(huì)block用戶進(jìn)程剿另,而是立刻返回一個(gè)error雨女。從用戶進(jìn)程角度講 氛堕,它發(fā)起一個(gè)read操作后,并不需要等待括儒,而是馬上就得到了一個(gè)結(jié)果帮寻。用戶進(jìn)程判斷結(jié)果是一個(gè)error時(shí)固逗,它就知道數(shù)據(jù)還沒有準(zhǔn)備好,于是它可以再次發(fā)送read操作掘鄙。一旦kernel中的數(shù)據(jù)準(zhǔn)備好了,并且又再次收到了用戶進(jìn)程的system call饿这,那么它馬上就將數(shù)據(jù)拷貝到了用戶內(nèi)存长捧,然后返回吻贿。

所以舅列,nonblocking IO的特點(diǎn)是用戶進(jìn)程需要不斷的主動(dòng)詢問kernel數(shù)據(jù)好了沒有帐要。

總結(jié):阻塞I/O模式下,雖然不會(huì)占用大量的CPU時(shí)間奋早,一個(gè)線程只能處理一個(gè)流的I/O事件耽装。如果想要同時(shí)處理多個(gè)流掉奄,要么多進(jìn)程(fork)挥萌,要么多線程(pthread_create)引瀑,很不幸這兩種方法效率都不高榨馁。于是再來考慮非阻塞忙輪詢的I/O方式,我們發(fā)現(xiàn)我們可以同時(shí)處理多個(gè)流了(把一個(gè)流從阻塞模式切換到非阻塞模式再此不予討論):

    for i in stream[]; {  
        if i has data  
        read until unavailable  
    }  
}

我們只要不停的把所有流從頭到尾問一遍屡萤,又從頭開始死陆。這樣就可以處理多個(gè)流了措译,但這樣的做法顯然不好领虹,因?yàn)槿绻械牧鞫紱]有數(shù)據(jù)求豫,那么只會(huì)白白浪費(fèi)CPU蝠嘉。
為了避免CPU空轉(zhuǎn)是晨,可以引進(jìn)了一個(gè)代理(一開始有一位叫做select的代理,后來又有一位叫做poll的代理蚊逢,不過兩者的本質(zhì)是一樣的)烙荷。這個(gè)代理比較厲害终抽,可以同時(shí)觀察許多流的I/O事件昼伴,在空閑的時(shí)候圃郊,會(huì)把當(dāng)前線程阻塞掉持舆,當(dāng)有一個(gè)或多個(gè)流有I/O事件時(shí),就從阻塞態(tài)中醒來逸寓,于是我們的程序就會(huì)輪詢一遍所有的流(于是我們可以把“忙”字去掉了)

while true {  
    select(streams[])  
    for i in streams[] {  
        if i has data  
        read until unavailable  
    }  
}

于是居兆,如果沒有I/O事件產(chǎn)生,我們的程序就會(huì)阻塞在select處竹伸。但是依然有個(gè)問題泥栖,我們從select那里僅僅知道了,有I/O事件發(fā)生了佩伤,但卻并不知道是那幾個(gè)流(可能有一個(gè)聊倔,多個(gè),甚至全部)生巡,我們只能無差別輪詢所有流见妒,找出能讀出數(shù)據(jù)孤荣,或者寫入數(shù)據(jù)的流,對(duì)他們進(jìn)行操作须揣。即使用select盐股,我們有O(n)的無差別輪詢復(fù)雜度,同時(shí)處理的流越多耻卡,沒一次無差別輪詢時(shí)間就越長(zhǎng)疯汁。
epoll可以理解為event poll,不同于忙輪詢和無差別輪詢卵酪,epoll之會(huì)把哪個(gè)流發(fā)生了怎樣的I/O事件通知我們幌蚊。此時(shí)我們對(duì)這些流的操作都是有意義的。(復(fù)雜度降低到了O(1))

I/O多路復(fù)用模型( IO multiplexing)

IO multiplexing就是我們說的select溃卡,poll溢豆,epoll,有些地方也稱這種IO方式為event driven IO瘸羡。select/epoll的好處就在于單個(gè)process就可以同時(shí)處理多個(gè)網(wǎng)絡(luò)連接的IO漩仙。它的基本原理就是select,poll犹赖,epoll這個(gè)function會(huì)不斷的輪詢所負(fù)責(zé)的所有socket队他,當(dāng)某個(gè)socket有數(shù)據(jù)到達(dá)了,就通知用戶進(jìn)程峻村。select麸折,poll,epoll這個(gè)function也會(huì)使進(jìn)程阻塞雀哨,但是和阻塞I/O所不同的的磕谅,這兩個(gè)函數(shù)可以同時(shí)阻塞多個(gè)I/O操作私爷。而且可以同時(shí)對(duì)多個(gè)讀操作,多個(gè)寫操作的I/O函數(shù)進(jìn)行檢測(cè)膊夹,直到有數(shù)據(jù)可讀或可寫時(shí)衬浑,才真正調(diào)用I/O操作函數(shù)。


image.png

當(dāng)用戶進(jìn)程調(diào)用了select放刨,那么整個(gè)進(jìn)程會(huì)被block工秩,而同時(shí),kernel會(huì)“監(jiān)視”所有select負(fù)責(zé)的socket进统,當(dāng)任何一個(gè)socket中的數(shù)據(jù)準(zhǔn)備好了助币,select就會(huì)返回。這個(gè)時(shí)候用戶進(jìn)程再調(diào)用read操作螟碎,將數(shù)據(jù)從kernel拷貝到用戶進(jìn)程眉菱。

所以,I/O 多路復(fù)用的特點(diǎn)是通過一種機(jī)制一個(gè)進(jìn)程能同時(shí)等待多個(gè)文件描述符(見《I/O模型之二:Linux IO模式及 select掉分、poll俭缓、epoll詳解》),而這些文件描述符(套接字描述符)其中的任意一個(gè)進(jìn)入讀就緒狀態(tài)酥郭,select()函數(shù)就可以返回华坦。

這個(gè)圖和blocking IO的圖其實(shí)并沒有太大的不同,事實(shí)上不从,還更差一些惜姐。因?yàn)檫@里需要使用兩個(gè)system call (select 和 recvfrom),而blocking IO只調(diào)用了一個(gè)system call (recvfrom)椿息。但是歹袁,用select的優(yōu)勢(shì)在于它可以同時(shí)處理多個(gè)connection。

所以撵颊,如果處理的連接數(shù)不是很高的話宇攻,使用select/epoll的web server不一定比使用multi-threading + blocking IO的web server性能更好,可能延遲還更大倡勇。select/epoll的優(yōu)勢(shì)并不是對(duì)于單個(gè)連接能處理得更快逞刷,而是在于能處理更多的連接。)

在IO multiplexing Model中妻熊,實(shí)際中夸浅,對(duì)于每一個(gè)socket,一般都設(shè)置成為non-blocking扔役,但是帆喇,如上圖所示,整個(gè)用戶的process其實(shí)是一直被block的亿胸。只不過process是被select這個(gè)函數(shù)block坯钦,而不是被socket IO給block预皇。

信號(hào)驅(qū)動(dòng)I/O模型

我們也可以用信號(hào),讓內(nèi)核在描述符就緒時(shí)發(fā)送SIGIO信號(hào)通知我們婉刀。通過sigaction系統(tǒng)調(diào)用安裝一個(gè)信號(hào)處理函數(shù)吟温。該系統(tǒng)調(diào)用將立即返回,我們的進(jìn)程繼續(xù)工作突颊,也就是說它沒有被阻塞鲁豪。當(dāng)數(shù)據(jù)報(bào)準(zhǔn)備好讀取時(shí),內(nèi)核就為該進(jìn)程產(chǎn)生一個(gè)SIGIO信號(hào)律秃。我們隨后既可以在信號(hào)處理函數(shù)中調(diào)用recvfrom讀取數(shù)據(jù)報(bào)爬橡,并通知主循環(huán)數(shù)據(jù)已經(jīng)準(zhǔn)備好待處理。
優(yōu)勢(shì):等待數(shù)據(jù)報(bào)到達(dá)期間進(jìn)程不被阻塞棒动。主循環(huán)可以繼續(xù)執(zhí)行糙申,只要等待來自信號(hào)處理函數(shù)的通知:既可以是數(shù)據(jù)已準(zhǔn)備好被處理,也可以是數(shù)據(jù)報(bào)已準(zhǔn)備好被讀取船惨。


image.png
異步I/O模型(asynchronous IO)

linux下的asynchronous IO其實(shí)用得很少郭宝。

告知內(nèi)核啟動(dòng)某個(gè)操作,并讓內(nèi)核在整個(gè)操作(包括將內(nèi)核復(fù)制到我們自己的緩沖區(qū))完成后通知我們掷漱。
與信號(hào)驅(qū)動(dòng)模型的主要區(qū)別在于:信號(hào)驅(qū)動(dòng)式I/O是由內(nèi)核通知我們何時(shí)可以啟動(dòng)一個(gè)I/O操作,而異步模型是由內(nèi)核通知我們I/O操作何時(shí)完成榄檬。


image.png

調(diào)用aio_read(Posix異步I/O函數(shù)以aio_或lio_開頭)函數(shù)卜范,給內(nèi)核傳遞描述字、緩沖區(qū)指針鹿榜、緩沖區(qū)大泻Q(與read相同的3個(gè)參數(shù))、文件偏移以及通知的方式舱殿,然后系統(tǒng)立即返回奥裸。我們的進(jìn)程不阻塞于等待I/0操作的完成。當(dāng)內(nèi)核將數(shù)據(jù)拷貝到緩沖區(qū)后沪袭,再通知應(yīng)用程序湾宙。

用戶進(jìn)程發(fā)起read操作之后,立刻就可以開始去做其它的事冈绊。而另一方面侠鳄,從kernel的角度,當(dāng)它受到一個(gè)asynchronous read之后死宣,首先它會(huì)立刻返回伟恶,所以不會(huì)對(duì)用戶進(jìn)程產(chǎn)生任何block。然后毅该,kernel會(huì)等待數(shù)據(jù)準(zhǔn)備完成博秫,然后將數(shù)據(jù)拷貝到用戶內(nèi)存潦牛,當(dāng)這一切都完成之后,kernel會(huì)給用戶進(jìn)程發(fā)送一個(gè)signal挡育,告訴它read操作完成了巴碗。

總結(jié)
各種I/O模型的對(duì)比:

blocking和non-blocking的區(qū)別
調(diào)用blocking IO會(huì)一直block住對(duì)應(yīng)的進(jìn)程直到操作完成,而non-blocking IO在kernel還準(zhǔn)備數(shù)據(jù)的情況下會(huì)立刻返回静盅。

synchronous IO和asynchronous IO的區(qū)別
在說明synchronous IO和asynchronous IO的區(qū)別之前良价,需要先給出兩者的定義。POSIX的定義是這樣子的:

  • A synchronous I/O operation causes the requesting process to be blocked until that I/O operation completes;
  • An asynchronous I/O operation does not cause the requesting process to be blocked;

兩者的區(qū)別就在于synchronous IO做”IO operation”的時(shí)候會(huì)將process阻塞蒿叠。按照這個(gè)定義明垢,之前所述的blocking IO,non-blocking IO市咽,IO multiplexing都屬于synchronous IO痊银。

有人會(huì)說,non-blocking IO并沒有被block啊施绎。這里有個(gè)非乘莞铮“狡猾”的地方,定義中所指的”IO operation”是指真實(shí)的IO操作谷醉,就是例子中的recvfrom這個(gè)system call致稀。non-blocking IO在執(zhí)行recvfrom這個(gè)system call的時(shí)候,如果kernel的數(shù)據(jù)沒有準(zhǔn)備好俱尼,這時(shí)候不會(huì)block進(jìn)程抖单。但是,當(dāng)kernel中數(shù)據(jù)準(zhǔn)備好的時(shí)候遇八,recvfrom會(huì)將數(shù)據(jù)從kernel拷貝到用戶內(nèi)存中矛绘,這個(gè)時(shí)候進(jìn)程是被block了,在這段時(shí)間內(nèi)刃永,進(jìn)程是被block的货矮。

而asynchronous IO則不一樣,當(dāng)進(jìn)程發(fā)起IO 操作之后斯够,就直接返回再也不理睬了囚玫,直到kernel發(fā)送一個(gè)信號(hào),告訴進(jìn)程說IO完成雳刺。在這整個(gè)過程中劫灶,進(jìn)程完全沒有被block。

各個(gè)IO Model的比較如圖所示:


image.png

通過上面的圖片掖桦,可以發(fā)現(xiàn)non-blocking IO和asynchronous IO的區(qū)別還是很明顯的本昏。在non-blocking IO中,雖然進(jìn)程大部分時(shí)間都不會(huì)被block枪汪,但是它仍然要求進(jìn)程去主動(dòng)的check涌穆,并且當(dāng)數(shù)據(jù)準(zhǔn)備完成以后怔昨,也需要進(jìn)程主動(dòng)的再次調(diào)用recvfrom來將數(shù)據(jù)拷貝到用戶內(nèi)存。而asynchronous IO則完全不同宿稀。它就像是用戶進(jìn)程將整個(gè)IO操作交給了他人(kernel)完成趁舀,然后他人做完后發(fā)信號(hào)通知。在此期間祝沸,用戶進(jìn)程不需要去檢查IO操作的狀態(tài)矮烹,也不需要主動(dòng)的去拷貝數(shù)據(jù)。

參考《unix網(wǎng)絡(luò)編程》

參考http://blog.csdn.net/blueboy2000/article/details/4485874

參考http://blog.csdn.net/suxinpingtao51/article/details/46314097

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末罩锐,一起剝皮案震驚了整個(gè)濱河市奉狈,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌涩惑,老刑警劉巖仁期,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異竭恬,居然都是意外死亡跛蛋,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門痊硕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來赊级,“玉大人,你說我怎么就攤上這事岔绸〈诵疲” “怎么了?”我有些...
    開封第一講書人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵亭螟,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我骑歹,道長(zhǎng)预烙,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任道媚,我火速辦了婚禮扁掸,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘最域。我一直安慰自己谴分,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開白布镀脂。 她就那樣靜靜地躺著牺蹄,像睡著了一般。 火紅的嫁衣襯著肌膚如雪薄翅。 梳的紋絲不亂的頭發(fā)上沙兰,一...
    開封第一講書人閱讀 49,950評(píng)論 1 291
  • 那天氓奈,我揣著相機(jī)與錄音,去河邊找鬼鼎天。 笑死舀奶,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的斋射。 我是一名探鬼主播育勺,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼罗岖!你這毒婦竟也來了涧至?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤呀闻,失蹤者是張志新(化名)和其女友劉穎化借,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體捡多,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蓖康,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了垒手。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蒜焊。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖科贬,靈堂內(nèi)的尸體忽然破棺而出泳梆,到底是詐尸還是另有隱情,我是刑警寧澤榜掌,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布优妙,位于F島的核電站,受9級(jí)特大地震影響憎账,放射性物質(zhì)發(fā)生泄漏套硼。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一胞皱、第九天 我趴在偏房一處隱蔽的房頂上張望邪意。 院中可真熱鬧,春花似錦反砌、人聲如沸雾鬼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽策菜。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間做入,已是汗流浹背冒晰。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留竟块,地道東北人壶运。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像浪秘,于是被迫代替她去往敵國(guó)和親蒋情。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350

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