I/O模型

I/O模型: 阻塞、非阻塞、I/O復(fù)用、同步、異步

I/O模型不論在實(shí)際使用還是準(zhǔn)備筆試面試中都是重要的內(nèi)容,參考Unix網(wǎng)絡(luò)編程進(jìn)行總結(jié)如下。

明確I/O考察的對(duì)象和流程

參考Unix網(wǎng)絡(luò)編程,一個(gè)輸入操作通常包括兩個(gè)不同的階段:

  1. 等待數(shù)據(jù)準(zhǔn)備好
  2. 從內(nèi)核向進(jìn)程復(fù)制數(shù)據(jù)

對(duì)于一個(gè)套接字的輸入操作,第一步通常涉及等待數(shù)據(jù)從網(wǎng)絡(luò)到達(dá),當(dāng)所等待分組到達(dá)時(shí),被復(fù)制到內(nèi)核的某個(gè)緩沖區(qū);
第二步就是把數(shù)據(jù)從內(nèi)核緩沖區(qū)復(fù)制到應(yīng)用進(jìn)程緩沖區(qū)。

理解上述兩個(gè)不同階段對(duì)于后續(xù)理解I/O模型尤其是非阻塞I/O與同步I/O關(guān)系十分必要。

I/O模型

1. 阻塞式I/O模型

阻塞式I/O是最流行的I/O,也是所有套接字默認(rèn)的I/O。


阻塞式IO.png

如圖所示,進(jìn)程調(diào)用recvfrom系統(tǒng)調(diào)用,直到數(shù)據(jù)報(bào)到達(dá)且被復(fù)制到應(yīng)用進(jìn)程緩沖區(qū)中或發(fā)生錯(cuò)誤才返回。
也就是說(shuō),進(jìn)程從調(diào)用recvfrom開(kāi)始到返回的整個(gè)時(shí)段都是阻塞的(上述兩個(gè)階段都是阻塞),recvfrom成功返回后,應(yīng)用進(jìn)程才開(kāi)始處理數(shù)據(jù)報(bào)。

2. 非阻塞I/O模型

當(dāng)所請(qǐng)求的I/O操作非得把本進(jìn)程投入睡眠才能完成時(shí),不投入睡眠,而是返回一個(gè)錯(cuò)誤。


非阻塞IO.png

如圖所示,不同于阻塞式I/O,非阻塞I/O在第一階段數(shù)據(jù)沒(méi)有準(zhǔn)備好的時(shí)候,不阻塞,而是直接返回一個(gè)錯(cuò)誤(EWOULDBLOCK)。
所以一般采用輪詢(polling)的方式,應(yīng)用進(jìn)程持續(xù)輪詢內(nèi)核,查看數(shù)據(jù)是否準(zhǔn)備好。當(dāng)數(shù)據(jù)準(zhǔn)備好時(shí),被復(fù)制到應(yīng)用進(jìn)程緩沖區(qū)(第二階段)。

注*:值得注意的一點(diǎn)是,當(dāng)?shù)谝浑A段數(shù)據(jù)準(zhǔn)備完成后,進(jìn)入第二階段,內(nèi)核向內(nèi)存的復(fù)制。這一階段仍然是阻塞的,這對(duì)于后續(xù)理解非阻塞與同步的關(guān)系十分重要。

3. I/O復(fù)用模型

I/O復(fù)用最常見(jiàn)的就是select和epoll,其阻塞發(fā)生在上述兩個(gè)系統(tǒng)調(diào)用之一,而不是真正的I/O系統(tǒng)調(diào)用上

IO復(fù)用.png

當(dāng)用戶進(jìn)程調(diào)用了select,那么整個(gè)進(jìn)程會(huì)被阻塞于select。內(nèi)核會(huì)“監(jiān)視”所有select負(fù)責(zé)的套接字,當(dāng)任何一個(gè)套接字中的數(shù)據(jù)準(zhǔn)備好了,select就會(huì)返回。

可以注意到select和epoll_wait會(huì)因?yàn)闆](méi)有數(shù)據(jù)而引發(fā)超時(shí)返回0的,所以這倆也是會(huì)阻塞,而且讀寫(xiě)數(shù)據(jù)時(shí)肯定要阻塞

這時(shí)候進(jìn)入第二階段,完成內(nèi)核向內(nèi)存的數(shù)據(jù)復(fù)制。
I/O復(fù)用的優(yōu)勢(shì)在于同時(shí)等待多個(gè)描述符就緒,單就一個(gè)描述符可言,其沒(méi)有優(yōu)勢(shì),反而還會(huì)因?yàn)槎嘁淮蝧elect系統(tǒng)調(diào)用存在劣勢(shì)。

4. 異步I/O模型

異步I/O的工作機(jī)制是告知內(nèi)核啟動(dòng)某個(gè)操作,并讓內(nèi)核在整個(gè)操作(包括第二階段數(shù)據(jù)從內(nèi)核向用戶進(jìn)程的復(fù)制)完成后告知我們。
如下圖所示

異步IO.png

異步I/O要通過(guò)調(diào)用特殊API實(shí)現(xiàn)(如POSIX的aio_read),可以看出,其在兩個(gè)階段都是沒(méi)有對(duì)于用戶進(jìn)程的阻塞的,依靠信號(hào)通知進(jìn)程整個(gè)過(guò)程完成。

5. ** 同步、異步與阻塞、非阻塞、I/O復(fù)用的關(guān)系**

首先先來(lái)再明確一下同步、異步I/O之間的區(qū)別。
書(shū)中所述,POSIX把兩種術(shù)語(yǔ)定義如下:

  • 同步I/O:導(dǎo)致請(qǐng)求進(jìn)程阻塞,直到I/O操作完成;
  • 異步I/O: 不導(dǎo)致請(qǐng)求進(jìn)程阻塞。
    所以說(shuō),阻塞式I/O, 非阻塞I/O, I/O復(fù)用由于都導(dǎo)致了請(qǐng)求進(jìn)程阻塞,所以均屬于同步I/O。

(值得注意的是非阻塞I/O,正如之前提示要注意的,其在第二階段內(nèi)核向內(nèi)存復(fù)制數(shù)據(jù)是會(huì)導(dǎo)致用戶進(jìn)程的阻塞,所以也屬于同步I/O)

想使用異步I/O,必須使用特殊的API(如linux下AIO,Windows下IOCP等)。


同步異步IO分類(lèi).png

總結(jié)

IO模型總結(jié).png

可以看出阻塞式、非阻塞式、與I/O復(fù)用,其不同之處在于第一階段,第二階段的處理方式相同(均阻塞與recvfrom調(diào)用),這也是剛才說(shuō)到的將他們歸于同步I/O的原因。
而異步I/O不存在請(qǐng)求進(jìn)程阻塞的情況。同時(shí)注意前三種I/O模型在第一階段的處理方式(阻塞,返回+輪詢,阻塞于select等),區(qū)分這三種I/O模型。

最后,再舉幾個(gè)不是很恰當(dāng)?shù)睦觼?lái)說(shuō)明這四個(gè)IO Model:
有A,B,C,D四個(gè)人在釣魚(yú):
A用的是最老式的魚(yú)竿,所以呢,得一直守著,等到魚(yú)上鉤了再拉桿;
B的魚(yú)竿有個(gè)功能,能夠顯示是否有魚(yú)上鉤,所以呢,B就和旁邊的MM聊天,隔會(huì)再看看有沒(méi)有魚(yú)上鉤,有的話就迅速拉桿;
C用的魚(yú)竿和B差不多,但他想了一個(gè)好辦法,就是同時(shí)放好幾根魚(yú)竿,然后守在旁邊,一旦有顯示說(shuō)魚(yú)上鉤了,它就將對(duì)應(yīng)的魚(yú)竿拉起來(lái);
D是個(gè)有錢(qián)人,干脆雇了一個(gè)人幫他釣魚(yú),一旦那個(gè)人把魚(yú)釣上來(lái)了,就給D發(fā)個(gè)短信。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容