unix下的I/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)
先說一下IO發(fā)生時所涉及的對象和步驟。
一個輸入操作通常包括下面兩個階段:
- 等待數(shù)據(jù)準(zhǔn)備好 (Waiting for the data to be ready)。對于一個套接口上的輸入操作,通常涉及等待數(shù)據(jù)從網(wǎng)絡(luò)到達(dá),到達(dá)后它被拷貝到內(nèi)核的某個緩沖區(qū)。
- 將數(shù)據(jù)從內(nèi)核緩存區(qū)拷貝到進(jìn)程緩沖區(qū)中 (Copying the data from the kernel to the process)
記住這兩個階段很重要,因?yàn)橐韵乱懻摰奈宸NIO Model的區(qū)別就是在兩個階段上各有不同的情況。
Blocking I/O Model(阻塞I/O)
默認(rèn)情況下所有的套接口都是blocking。
進(jìn)程調(diào)用recvfrom,其系統(tǒng)調(diào)用直到數(shù)據(jù)報到達(dá)(第一階段)且被拷貝到應(yīng)用進(jìn)程的緩沖區(qū)中(第二階段)或者發(fā)生錯誤(最常見的錯誤是系統(tǒng)調(diào)用被信號中斷)才返回。進(jìn)程在從調(diào)用recvfrom開始到它返回的整個過程是被阻塞的。 recvfrom成功返回后,應(yīng)用進(jìn)程開始處理數(shù)據(jù)報。
Nonblocking I/O Model(非阻塞I/O)
前三次調(diào)用recvfrom時數(shù)據(jù)還沒準(zhǔn)備好,這是內(nèi)核立即返回一個EWOULDBLOCK錯誤。第四次調(diào)用recvfrom時數(shù)據(jù)已準(zhǔn)備好,它被拷貝到應(yīng)用進(jìn)程緩沖區(qū),recvfrom接著成功返回,然后應(yīng)用進(jìn)程開始處理數(shù)據(jù)報。
這里最關(guān)鍵的一個操作就是輪詢(polling)。應(yīng)用進(jìn)程持續(xù)輪詢內(nèi)核,以查看數(shù)據(jù)是否就緒。這樣做往往會耗費(fèi)大量的CPU時間,這種模型通常會在專門提供某種功能的系統(tǒng)才有。
I/O Multiplexing Model(I/O復(fù)用模型)
當(dāng)用戶進(jìn)程調(diào)用了select,那么整個進(jìn)程會被block,而同時,kernel會“監(jiān)視”所有select負(fù)責(zé)的socket,當(dāng)任何一個socket中的數(shù)據(jù)準(zhǔn)備好了,select就會返回套接字可讀這個條件,我們調(diào)用recvfrom把所讀數(shù)據(jù)報拷貝到應(yīng)用程序進(jìn)程緩沖區(qū)。
和blocking IO的圖比較,I/O復(fù)用并沒有顯示出什么優(yōu)勢。事實(shí)上,可能稍有劣勢。因?yàn)檫@里需要使用兩個system call (select 和 recvfrom),而blocking IO只調(diào)用了一個system call。但是,用select的優(yōu)勢在于它可以同時處理多個connection。
Signal-Driven I/O Model(信號驅(qū)動I/O模型)
我們首先開啟套接口的信號驅(qū)動I/O功能,并通過sigaction系統(tǒng)調(diào)用安裝一個信號處理函數(shù)。該系統(tǒng)調(diào)用將立即返回,我們的進(jìn)程這是并沒有被阻塞,而是繼續(xù)執(zhí)行。當(dāng)數(shù)據(jù)報準(zhǔn)備好讀取時,內(nèi)核就為該進(jìn)程產(chǎn)生一個SIGIO信號。我們隨后既可以在信號處理函數(shù)中調(diào)用recvfrom讀取數(shù)據(jù)報,并通知主循環(huán)數(shù)據(jù)已準(zhǔn)備好待處理,也可以立即通知主循環(huán),讓它來讀取數(shù)據(jù)報。無論如何處理SIGIO信號,這種模型的優(yōu)勢在于等待數(shù)據(jù)報到達(dá)(第一階段)期間,進(jìn)程可以繼續(xù)執(zhí)行,不被阻塞。
Asynchronous I/O Model(異步I/O模型)
進(jìn)程發(fā)起read操作之后,立刻就可以開始去做其它的事。而另一方面,從kernel的角度,當(dāng)它受到一個asynchronous read之后,首先它會立刻返回,所以不會對用戶進(jìn)程產(chǎn)生任何block。然后,kernel會等待數(shù)據(jù)準(zhǔn)備完成,然后將數(shù)據(jù)拷貝到用戶內(nèi)存,當(dāng)這一切都完成之后,kernel會給用戶進(jìn)程發(fā)送一個signal,告訴它read操作完成了。
這個模型工作機(jī)制是:告訴內(nèi)核啟動某個操作,并讓內(nèi)核在整個操作(包括第二階段,即將數(shù)據(jù)從內(nèi)核拷貝到進(jìn)程緩沖區(qū)中)完成后通知我們。
這種模型和前一種模型區(qū)別在于:信號驅(qū)動I/O是由內(nèi)核通知我們何時可以啟動一個I/O操作,而異步I/O模型是由內(nèi)核通知我們I/O操作何時完成。
五種I/O模型介紹完了,下面來說說blocking和non-blocking的區(qū)別在哪,synchronous IO和asynchronous IO的區(qū)別在哪。
blocking I/Ovs non-blocking I/O :調(diào)用blocking IO會一直block住對應(yīng)的進(jìn)程直到操作完成,而non-blocking IO在kernel還準(zhǔn)備數(shù)據(jù)的情況下會立刻返回。
synchronous I/O vs asynchronous I/O:
先看看這兩個定義:
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”的時候會將process阻塞。按照這個定義,之前所述前四種模型blocking I/O,non-blocking I/O,IO multiplexing,signal driven IO都屬于synchronous IO。有人可能會說,non-blocking IO并沒有被block啊。這里有個非常“狡猾”的地方,定義中所指的”IO operation”是指真實(shí)的IO操作,就是例子中的recvfrom這個system call。non-blocking IO在執(zhí)行recvfrom這個system call的時候,如果kernel的數(shù)據(jù)沒有準(zhǔn)備好,這時候不會block進(jìn)程。但是,當(dāng)kernel中數(shù)據(jù)準(zhǔn)備好的時候,recvfrom會將數(shù)據(jù)從 kernel拷貝到用戶內(nèi)存中,這個時候(第二階段)進(jìn)程是被block了,在這段時間內(nèi),進(jìn)程是被block的。而asynchronous IO則不一樣,當(dāng)進(jìn)程發(fā)起IO 操作之后,就直接返回再也不理睬了,直到kernel發(fā)送一個信號,告訴進(jìn)程說IO完成。在這整個過程中,進(jìn)程完全沒有被block。
各個IO Model的比較如圖所示:
前4種模型的主要區(qū)別在于第一階段,因?yàn)樗鼈兊牡诙A段都是一樣的:在數(shù)據(jù)從內(nèi)核緩沖區(qū)拷貝到進(jìn)程緩沖區(qū)期間,進(jìn)程阻塞與recvfrom這個系統(tǒng)調(diào)用中。