原文:https://www.jianshu.com/p/b8203d46895c 我們?cè)谶M(jìn)行編程開(kāi)發(fā)的時(shí)候,經(jīng)常會(huì)涉及到同步,異步,阻塞,非阻塞,IO多路復(fù)用等概念,這幾個(gè)概念有區(qū)別,但是有時(shí)候也容易混淆,如果不總結(jié)一下的話很容易受到困擾,下面就記錄一下這幾個(gè)概念的理解。 Unix網(wǎng)絡(luò)編程中的五種IO模型
由于signal driven IO在實(shí)際使用中并不常用,所以這里只討論剩下的四種IO模型。 在討論之前先說(shuō)明一下IO發(fā)生時(shí)涉及到的對(duì)象和步驟,對(duì)于一個(gè)network IO,它會(huì)涉及到兩個(gè)系統(tǒng)對(duì)象:
那他們經(jīng)歷的兩個(gè)交互過(guò)程是:
之所以會(huì)有同步、異步、阻塞和非阻塞這幾種說(shuō)法就是根據(jù)程序在這兩個(gè)階段的處理方式不同而產(chǎn)生的。了解了這些背景之后,我們就分別針對(duì)四種IO模型進(jìn)行講解 Blocking IO - 阻塞IO在linux中,默認(rèn)情況下所有的socket都是blocking,一個(gè)典型的讀操作流程大概如下圖: 當(dāng)用戶(hù)進(jìn)程調(diào)用了recvfrom這個(gè)系統(tǒng)調(diào)用,kernel就開(kāi)始了IO的第一個(gè)階段:準(zhǔn)備數(shù)據(jù)。對(duì)于network IO來(lái)說(shuō),很多時(shí)候數(shù)據(jù)在一開(kāi)始還沒(méi)有到達(dá)(比如,還沒(méi)有收到一個(gè)完整的UDP包),這個(gè)時(shí)候kernel就要等待足夠的數(shù)據(jù)到來(lái)。而在用戶(hù)進(jìn)程這邊,整個(gè)進(jìn)程會(huì)被阻塞。當(dāng)kernel一直等到數(shù)據(jù)準(zhǔn)備好了,它就會(huì)將數(shù)據(jù)從kernel中拷貝到用戶(hù)內(nèi)存,然后kernel返回結(jié)果,用戶(hù)進(jìn)程才解除block的狀態(tài),重新運(yùn)行起來(lái)。 所以,blocking IO的特點(diǎn)就是在IO執(zhí)行的兩個(gè)階段都被block了。 NoneBlockingIO - 非阻塞IOlinux下,可以通過(guò)設(shè)置socket使其變?yōu)閚on-blocking。當(dāng)對(duì)一個(gè)non-blocking socket執(zhí)行讀操作時(shí),流程是這個(gè)樣子: 從圖中可以看出,當(dāng)用戶(hù)進(jìn)程發(fā)出recvfrom這個(gè)系統(tǒng)調(diào)用后,如果kernel中的數(shù)據(jù)還沒(méi)有準(zhǔn)備好,那么它并不會(huì)block用戶(hù)進(jìn)程,而是立刻返回一個(gè)結(jié)果(no datagram ready)。從用戶(hù)進(jìn)程角度講 ,它發(fā)起一個(gè)操作后,并沒(méi)有等待,而是馬上就得到了一個(gè)結(jié)果。用戶(hù)進(jìn)程得知數(shù)據(jù)還沒(méi)有準(zhǔn)備好后,它可以每隔一段時(shí)間再次發(fā)送recvfrom操作。一旦kernel中的數(shù)據(jù)準(zhǔn)備好了,并且又再次收到了用戶(hù)進(jìn)程的system call,那么它馬上就將數(shù)據(jù)拷貝到了用戶(hù)內(nèi)存,然后返回。 所以,用戶(hù)進(jìn)程其實(shí)是需要不斷的主動(dòng)詢(xún)問(wèn)kernel數(shù)據(jù)好了沒(méi)有。 IO multiplexing - IO多路復(fù)用I/O多路復(fù)用(multiplexing)是網(wǎng)絡(luò)編程中最常用的模型,像我們最常用的select、epoll都屬于這種模型。以select為例: 看起來(lái)它與blocking I/O很相似,兩個(gè)階段都阻塞。但它與blocking I/O的一個(gè)重要區(qū)別就是它可以等待多個(gè)數(shù)據(jù)報(bào)就緒(datagram ready),即可以處理多個(gè)連接。這里的select相當(dāng)于一個(gè)“代理”,調(diào)用select以后進(jìn)程會(huì)被select阻塞,這時(shí)候在內(nèi)核空間內(nèi)select會(huì)監(jiān)聽(tīng)指定的多個(gè)datagram (如socket連接),如果其中任意一個(gè)數(shù)據(jù)就緒了就返回。此時(shí)程序再進(jìn)行數(shù)據(jù)讀取操作,將數(shù)據(jù)拷貝至當(dāng)前進(jìn)程內(nèi)。由于select可以監(jiān)聽(tīng)多個(gè)socket,我們可以用它來(lái)處理多個(gè)連接。 在select模型中每個(gè)socket一般都設(shè)置成non-blocking,雖然等待數(shù)據(jù)階段仍然是阻塞狀態(tài),但是它是被select調(diào)用阻塞的,而不是直接被I/O阻塞的。select底層通過(guò)輪詢(xún)機(jī)制來(lái)判斷每個(gè)socket讀寫(xiě)是否就緒。 當(dāng)然select也有一些缺點(diǎn),比如底層輪詢(xún)機(jī)制會(huì)增加開(kāi)銷(xiāo)、支持的文件描述符數(shù)量過(guò)少等。為此,Linux引入了epoll作為select的改進(jìn)版本。 asynchronous IO - 異步IO異步I/O在網(wǎng)絡(luò)編程中幾乎用不到,在File I/O中可能會(huì)用到: 這里面的讀取操作的語(yǔ)義與上面的幾種模型都不同。這里的讀取操作(aio_read)會(huì)通知內(nèi)核進(jìn)行讀取操作并將數(shù)據(jù)拷貝至進(jìn)程中,完事后通知進(jìn)程整個(gè)操作全部完成(綁定一個(gè)回調(diào)函數(shù)處理數(shù)據(jù))。讀取操作會(huì)立刻返回,程序可以進(jìn)行其它的操作,所有的讀取、拷貝工作都由內(nèi)核去做,做完以后通知進(jìn)程,進(jìn)程調(diào)用綁定的回調(diào)函數(shù)來(lái)處理數(shù)據(jù)。 總結(jié)我們來(lái)總結(jié)一下阻塞、非阻塞,同步和異步這兩組概念。 先來(lái)說(shuō)阻塞和非阻塞:
再說(shuō)一說(shuō)同步和異步:
下面的這張圖很好地總結(jié)了之前講的這五種I/O模型(來(lái)自Unix Network Programming) 最后,在舉個(gè)簡(jiǎn)單的例子幫助理解,比如我們?cè)鯓咏鉀Q午飯問(wèn)題: A君喜歡下館子吃飯,服務(wù)員點(diǎn)完餐后,A君一直坐在座位上等待廚師炒菜,什么事情也沒(méi)有干,過(guò)了一會(huì)服務(wù)員端上飯菜后,A君就開(kāi)吃了 -- 【阻塞I/O】 B君也喜歡下館子,服務(wù)員點(diǎn)完餐后,B君看這個(gè)服務(wù)員姿色不錯(cuò),便一直和服務(wù)員聊人生理想,并時(shí)不時(shí)的打聽(tīng)自己的飯做好了沒(méi)有,過(guò)了一會(huì)飯也做好了,B君也撩到了美女服務(wù)員的微信號(hào) -- 【非阻塞I/O 】順便撩了個(gè)妹子? C君同樣喜歡下館子吃飯,但是C君不喜歡一個(gè)人下館子吃,要呼朋喚友一起下館子,但是這幫人到了飯店之后,每個(gè)人只點(diǎn)自己的,服務(wù)員一起給他們下單后,就交給后廚去做了,每做好一個(gè)人的,服務(wù)員就負(fù)責(zé)給他們端上來(lái)。做他們的服務(wù)員真滴好累?? -- 【IO多路復(fù)用】 D君比較宅,不喜歡下館子,那怎么辦呢?美團(tuán)外賣(mài)?。ù颂帒?yīng)有廣告費(fèi):-D)手機(jī)下單后,自己啥也不用操心,只要等快遞小哥上門(mén)就行了,這段時(shí)間可以擼好幾把王者農(nóng)藥的了,嘿嘿 -- 【異步IO】 近期熱文 如有侵權(quán),請(qǐng)及時(shí)聯(lián)系 |
|
來(lái)自: 太極混元天尊 > 《學(xué)習(xí)資料》