背景
阻塞
阻塞這個(gè)詞來自操作系統(tǒng)的線程/進(jìn)程的狀態(tài)模型中浪腐,如下圖:
一個(gè)線程/進(jìn)程經(jīng)歷的5個(gè)狀態(tài)纵揍,創(chuàng)建,就緒议街,運(yùn)行泽谨,阻塞,終止特漩。
各個(gè)狀態(tài)的轉(zhuǎn)換條件如上圖吧雹,其中有個(gè)阻塞狀態(tài),就是說當(dāng)線程中調(diào)用某個(gè)函數(shù)涂身,需要I/O請求雄卷,或者暫時(shí)得不到競爭資源的,操作系統(tǒng)會(huì)把該線程阻塞起來蛤售,避免浪費(fèi)CPU資源丁鹉,等到得到了資源,再變成就緒狀態(tài)悴能,等待CPU調(diào)度運(yùn)行揣钦。
UNIX I/O模型
Unix下可用的5種I/O模型:
(1)阻塞式I/O
(2)非阻塞式I/O
(3)I/O復(fù)用
(4)信號驅(qū)動(dòng)式I/O
(5)異步I/O
輸入操作
一個(gè)輸入操作通常包括兩個(gè)不同的階段:
(1)等待數(shù)據(jù)準(zhǔn)備好
(2)從內(nèi)核向進(jìn)程復(fù)制數(shù)據(jù)
而對于一個(gè)套接字上的輸入操作:
(1)等待數(shù)據(jù)從網(wǎng)絡(luò)中到達(dá),當(dāng)所等待分組到達(dá)時(shí)搜骡,它被復(fù)制到內(nèi)核中的某個(gè)緩沖區(qū)
(2)把數(shù)據(jù)從內(nèi)核緩沖區(qū)復(fù)制到應(yīng)用進(jìn)程緩沖區(qū)
1. 阻塞式I/O模型(blocking I/O)
我們把recvfrom函數(shù)視為系統(tǒng)調(diào)用拂盯,因?yàn)槲覀儏^(qū)分了應(yīng)用程序和內(nèi)核。
不論它如何實(shí)現(xiàn)记靡,一般都會(huì)從在應(yīng)用進(jìn)程空間中運(yùn)行切換到內(nèi)核空間中運(yùn)行谈竿,
一段時(shí)間之后再切換回來。
上圖中摸吠,進(jìn)程調(diào)用recvfrom空凸,其系統(tǒng)調(diào)用直到數(shù)據(jù)報(bào)到達(dá)且被復(fù)制到應(yīng)用進(jìn)程的緩沖區(qū)中或者發(fā)生錯(cuò)誤才返回。最常見的錯(cuò)誤是系統(tǒng)調(diào)用被信號中斷寸痢。我們說呀洲,進(jìn)程在從調(diào)用recvfrom開始到它返回的整段時(shí)間內(nèi)是被阻塞的。recvfrom成功返回后,應(yīng)用進(jìn)程開始處理數(shù)據(jù)報(bào)道逗。
2. 非阻塞式I/O模型(nonblocking I/O)
前三次調(diào)用recvfrom時(shí)沒有數(shù)據(jù)可返回兵罢,因此內(nèi)核轉(zhuǎn)而立即返回一個(gè)EWOULDBLOCK錯(cuò)誤。第四次調(diào)用recvfrom時(shí)已有一個(gè)數(shù)據(jù)報(bào)準(zhǔn)備好滓窍,它被復(fù)制到應(yīng)用進(jìn)程緩沖區(qū)卖词,于是recvfrom成功返回。
當(dāng)一個(gè)應(yīng)用進(jìn)程像這樣對一個(gè)非阻塞描述符循環(huán)調(diào)用recvfrom時(shí)吏夯,我們稱之為輪詢(polling)此蜈。應(yīng)用進(jìn)程持續(xù)輪詢內(nèi)核,以查看某個(gè)操作是否就緒噪生。這么做往往耗費(fèi)大量CPU時(shí)間裆赵,不過這種模型偶爾也會(huì)遇到,通常是在專門提供某一功能的系統(tǒng)中才有跺嗽。
3. I/O復(fù)用模型(I/O multiplexing)
有了I/O復(fù)用战授,我們就可以調(diào)用select或poll,阻塞在這兩個(gè)系統(tǒng)調(diào)用中的某一個(gè)之上抛蚁,而不是阻塞在真正的I/O系統(tǒng)調(diào)用之上陈醒。
我們阻塞于select調(diào)用,等待數(shù)據(jù)報(bào)套接字變?yōu)榭勺x瞧甩。當(dāng)select返回套接字可讀這一條件時(shí)钉跷,我們調(diào)用recvfrom把所讀數(shù)據(jù)報(bào)復(fù)制到應(yīng)用進(jìn)程緩沖區(qū)。select的優(yōu)勢在于我們可以等待多個(gè)描述符就緒肚逸。
4. 信號驅(qū)動(dòng)式I/O模型(signal-driven I/O)
我們可以使用信號爷辙,讓內(nèi)核在描述符就緒時(shí)發(fā)送SIGIO信號通知我們。
我們稱這種模型為信號驅(qū)動(dòng)式I/O朦促。
我們首先通過sigaction系統(tǒng)調(diào)用安裝一個(gè)信號處理函數(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信號禀忆。我們隨后既可以在信號處理函數(shù)中調(diào)用recvfrom讀取數(shù)據(jù)報(bào)臊旭,并通知主循環(huán)數(shù)據(jù)已準(zhǔn)備好待處理,也可以立即通知主循環(huán)箩退,讓它讀取數(shù)據(jù)報(bào)离熏。
無論如何處理SIGIO信號,這種模型的優(yōu)勢在于等待數(shù)據(jù)報(bào)到達(dá)期間進(jìn)程不被阻塞戴涝。主循環(huán)可以繼續(xù)執(zhí)行滋戳,只要等待來自信號處理函數(shù)的通知钻蔑。既可以是數(shù)據(jù)已準(zhǔn)備好被處理,也可以是數(shù)據(jù)報(bào)已準(zhǔn)備好被讀取奸鸯。
5. 異步I/O模型(asynchronous I/O)
異步I/O模型中咪笑,首先告知內(nèi)核啟動(dòng)某個(gè)操作,并讓內(nèi)核在整個(gè)操作完成后通知我們娄涩。這種模型與信號驅(qū)動(dòng)模型的主要區(qū)別在于:信號驅(qū)動(dòng)式I/O是由內(nèi)核通知我們何時(shí)可以啟動(dòng)一個(gè)I/O操作蒲肋,而異步I/O模型是由內(nèi)核通知我們I/O操作何時(shí)完成。
aio_read系統(tǒng)調(diào)用立即返回钝满,而且在等待I/O完成期間,我們的進(jìn)程不被阻塞申窘。
各種I/O模型比較
我們可以看出弯蚜,前4種模型主要區(qū)別在于第一階段,因?yàn)樗鼈兊牡诙A段是一樣的剃法。在數(shù)據(jù)從內(nèi)核復(fù)制到調(diào)用者的緩沖區(qū)期間碎捺,進(jìn)程阻塞于recvfrom調(diào)用。
相反贷洲,異步I/O模型在這兩個(gè)階段都要處理收厨,從而不同于其他4種模型。
POSIX把這兩個(gè)數(shù)據(jù)定義如下:
(1)同步I/O操作(synchronous I/O operation)導(dǎo)致請求進(jìn)程阻塞优构,直到I/O操作完成诵叁。
(2)異步I/O操作(asynchronous I/O operation)不導(dǎo)致請求進(jìn)程阻塞。
根據(jù)上述定義钦椭,我們的前4種模型——阻塞式I/O模型拧额,非阻塞式I/O模型,I/O復(fù)用模型和信號驅(qū)動(dòng)式I/O模型彪腔,都是同步I/O模型侥锦,因?yàn)槠渲姓嬲腎/O操作(recvfrom)將阻塞進(jìn)程。只有異步I/O模型與POSIX定義的異步I/O匹配德挣。