阻塞、非阻塞、同步、異步 這四個(gè)概念很多人不能完全區(qū)別開(kāi),尤其是他們相互組合時(shí)同步阻塞,異步阻塞……
轉(zhuǎn)自:http://blog.chinaunix.net/uid-26606708-id-3175228.html
前4種模型的主要區(qū)別在于第一階段,因?yàn)樗鼈兊牡诙A段都是一樣的:在數(shù)據(jù)從內(nèi)核緩沖區(qū)拷貝到進(jìn)程緩沖區(qū)期間,進(jìn)程阻塞與recvfrom這個(gè)系統(tǒng)調(diào)用中。
參考 Richard Stevens的“UNIX® Network Programming Volume 1, Third Edition: The Sockets Networking”。
Stevens一共提出了五種 IO Model:
- blocking IO
- nonblocking IO
- IO multiplexing (select and poll)
- signal driven IO (SIGIO)
- asynchronous IO (the POSIX aio_functions)
先說(shuō)一下IO發(fā)生時(shí)所涉及的對(duì)象和步驟。
一個(gè)輸入操作通常包括下面兩個(gè)階段:
- 等待數(shù)據(jù)準(zhǔn)備好 (Waiting for the data to be ready)。對(duì)于一個(gè)套接口上的輸入操作,通常涉及等待數(shù)據(jù)從網(wǎng)絡(luò)到達(dá),到達(dá)后它被拷貝到內(nèi)核的某個(gè)緩沖區(qū)。
- 將數(shù)據(jù)從內(nèi)核緩存區(qū)拷貝到進(jìn)程緩沖區(qū)中 (Copying the data from the kernel to the process)
記住這兩個(gè)階段很重要,因?yàn)橐韵乱懻摰奈宸NIO Model的區(qū)別就是在兩個(gè)階段上各有不同的情況。
Blocking I/O Model(阻塞I/O)
默認(rèn)情況下所有的套接口都是blocking。

進(jìn)程調(diào)用recvfrom,其系統(tǒng)調(diào)用直到數(shù)據(jù)報(bào)到達(dá)(第一階段)且被拷貝到應(yīng)用進(jìn)程的緩沖區(qū)中(第二階段)或者發(fā)生錯(cuò)誤(最常見(jiàn)的錯(cuò)誤是系統(tǒng)調(diào)用被信號(hào)中斷)才返回。進(jìn)程在從調(diào)用recvfrom開(kāi)始到它返回的整個(gè)過(guò)程是被阻塞的。 recvfrom成功返回后,應(yīng)用進(jìn)程開(kāi)始處理數(shù)據(jù)報(bào)。
Nonblocking I/O Model(非阻塞I/O)

前三次調(diào)用recvfrom時(shí)數(shù)據(jù)還沒(méi)準(zhǔn)備好,這是內(nèi)核立即返回一個(gè)EWOULDBLOCK錯(cuò)誤。第四次調(diào)用recvfrom時(shí)數(shù)據(jù)已準(zhǔn)備好,它被拷貝到應(yīng)用進(jìn)程緩沖區(qū),recvfrom接著成功返回,然后應(yīng)用進(jìn)程開(kāi)始處理數(shù)據(jù)報(bào)。
這里最關(guān)鍵的一個(gè)操作就是輪詢(xún)(polling)。應(yīng)用進(jìn)程持續(xù)輪詢(xún)內(nèi)核,以查看數(shù)據(jù)是否就緒。這樣做往往會(huì)耗費(fèi)大量的CPU時(shí)間,這種模型通常會(huì)在專(zhuān)門(mén)提供某種功能的系統(tǒng)才有。
I/O Multiplexing Model(I/O復(fù)用模型)

當(dāng)用戶(hù)進(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è)條件,我們調(diào)用recvfrom把所讀數(shù)據(jù)報(bào)拷貝到應(yīng)用程序進(jìn)程緩沖區(qū)。
和blocking IO的圖比較,I/O復(fù)用并沒(méi)有顯示出什么優(yōu)勢(shì)。事實(shí)上,可能稍有劣勢(shì)。因?yàn)檫@里需要使用兩個(gè)system call (select 和 recvfrom),而blocking IO只調(diào)用了一個(gè)system call。但是,用select的優(yōu)勢(shì)在于它可以同時(shí)處理多個(gè)connection。(peakflys注: 常用的服務(wù)器網(wǎng)絡(luò)模型即是 多路復(fù)用IO調(diào)度+非阻塞fd+線(xiàn)程池)
Signal-Driven I/O Model(信號(hào)驅(qū)動(dòng)I/O模型)

我們首先開(kāi)啟套接口的信號(hào)驅(qū)動(dòng)I/O功能,并通過(guò)sigaction系統(tǒng)調(diào)用安裝一個(gè)信號(hào)處理函數(shù)。該系統(tǒng)調(diào)用將立即返回,我們的進(jìn)程這是并沒(méi)有被阻塞,而是繼續(xù)執(zhí)行。當(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ù)已準(zhǔn)備好待處理,也可以立即通知主循環(huán),讓它來(lái)讀取數(shù)據(jù)報(bào)。無(wú)論如何處理SIGIO信號(hào),這種模型的優(yōu)勢(shì)在于等待數(shù)據(jù)報(bào)到達(dá)(第一階段)期間,進(jìn)程可以繼續(xù)執(zhí)行,不被阻塞。
Asynchronous I/O Model(異步I/O模型)

進(jìn)程發(fā)起read操作之后,立刻就可以開(kāi)始去做其它的事。而另一方面,從kernel的角度,當(dāng)它受到一個(gè)asynchronous read之后,首先它會(huì)立刻返回,所以不會(huì)對(duì)用戶(hù)進(jìn)程產(chǎn)生任何block。然后,kernel會(huì)等待數(shù)據(jù)準(zhǔn)備完成,然后將數(shù)據(jù)拷貝到用戶(hù)內(nèi)存,當(dāng)這一切都完成之后,kernel會(huì)給用戶(hù)進(jìn)程發(fā)送一個(gè)signal,告訴它read操作完成了。
這個(gè)模型工作機(jī)制是:告訴內(nèi)核啟動(dòng)某個(gè)操作,并讓內(nèi)核在整個(gè)操作(包括第二階段,即將數(shù)據(jù)從內(nèi)核拷貝到進(jìn)程緩沖區(qū)中)完成后通知我們。
這種模型和前一種模型區(qū)別在于:信號(hào)驅(qū)動(dòng)I/O是由內(nèi)核通知我們何時(shí)可以啟動(dòng)一個(gè)I/O操作,而異步I/O模型是由內(nèi)核通知我們I/O操作何時(shí)完成。
五種I/O模型介紹完了,下面來(lái)說(shuō)說(shuō)blocking和non-blocking的區(qū)別在哪,synchronous IO和asynchronous IO的區(qū)別在哪。
blocking I/Ovs non-blocking I/O :調(diào)用blocking IO會(huì)一直block住對(duì)應(yīng)的進(jìn)程直到操作完成,而non-blocking IO在kernel還準(zhǔn)備數(shù)據(jù)的情況下會(huì)立刻返回。
synchronous I/O vs asynchronous I/O:
先看看這兩個(gè)定義:
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 I/O,non-blocking I/O,IO multiplexing,signal driven IO都屬于synchronous IO。有人可能會(huì)說(shuō),non-blocking IO并沒(méi)有被block啊。這里有個(gè)非常“狡猾”的地方,定義中所指的”IO operation”是指真實(shí)的IO操作,就是例子中的recvfrom這個(gè)system call。non-blocking IO在執(zhí)行recvfrom這個(gè)system call的時(shí)候,如果kernel的數(shù)據(jù)沒(méi)有準(zhǔn)備好,這時(shí)候不會(huì)block進(jìn)程。但是,當(dāng)kernel中數(shù)據(jù)準(zhǔn)備好的時(shí)候,recvfrom會(huì)將數(shù)據(jù)從 kernel拷貝到用戶(hù)內(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)程說(shuō)IO完成。在這整個(gè)過(guò)程中,進(jìn)程完全沒(méi)有被block。
各個(gè)IO Model的比較如圖所示:

前4種模型的主要區(qū)別在于第一階段,因?yàn)樗鼈兊牡诙A段都是一樣的:在數(shù)據(jù)從內(nèi)核緩沖區(qū)拷貝到進(jìn)程緩沖區(qū)期間,進(jìn)程阻塞與recvfrom這個(gè)系統(tǒng)調(diào)用中。
(peakflys注:異步調(diào)用需要指定回調(diào)函數(shù)或者信號(hào)處理函數(shù),所以和其他幾個(gè)模型的函數(shù)調(diào)用有明顯不同,linux下的異步調(diào)用庫(kù)有兩套,一套是linux內(nèi)核提供的,一套是glibc提供的,前者執(zhí)行異步調(diào)用時(shí)是進(jìn)入系統(tǒng)內(nèi)核,統(tǒng)一有IO調(diào)度器調(diào)度,后者是在調(diào)用進(jìn)程里生成一個(gè)新的線(xiàn)程執(zhí)行異步操作,同fd的異步操作在同一個(gè)線(xiàn)程執(zhí)行)