理解一下5種IO模型、阻塞IO和非阻塞IO、同步IO和異步IO
? ? ?
? ?正文? ?
5種IO模型、阻塞IO和非阻塞IO、同步IO和異步IO
看了一些文章,發(fā)現(xiàn)有很多不同的理解,可能是因?yàn)榇蠹胰肭械慕嵌取h(huán)境不一樣。所以,我們先說明基本的IO操作及環(huán)境。
本文是在《UNIX網(wǎng)絡(luò)編程 卷1:套接字聯(lián)網(wǎng)API》6.2節(jié)"I/O 模型 "的基礎(chǔ)上,即UNIX/LINUX環(huán)境下的網(wǎng)絡(luò) IO環(huán)境下的理解,它里面給出的例子是讀取(接收)網(wǎng)絡(luò)UDP數(shù)據(jù)。下面簡單寫寫自己對這些IO模型的理解。
1、IO

IO (Input/Output,輸入/輸出)即數(shù)據(jù)的讀取(接收)或?qū)懭耄òl(fā)送)操作,通常用戶進(jìn)程中的一個(gè)完整IO分為兩階段:用戶進(jìn)程空間<-->內(nèi)核空間、內(nèi)核空間<-->設(shè)備空間(磁盤、網(wǎng)絡(luò)等)。IO有內(nèi)存IO、網(wǎng)絡(luò)IO和磁盤IO三種,通常我們說的IO指的是后兩者。
LINUX中進(jìn)程無法直接操作I/O設(shè)備,其必須通過系統(tǒng)調(diào)用請求kernel來協(xié)助完成I/O動作;內(nèi)核會為每個(gè)I/O設(shè)備維護(hù)一個(gè)緩沖區(qū)。
對于一個(gè)輸入操作來說,進(jìn)程IO系統(tǒng)調(diào)用后,內(nèi)核會先看緩沖區(qū)中有沒有相應(yīng)的緩存數(shù)據(jù),沒有的話再到設(shè)備中讀取,因?yàn)樵O(shè)備IO一般速度較慢,需要等待;內(nèi)核緩沖區(qū)有數(shù)據(jù)則直接復(fù)制到進(jìn)程空間。
所以,對于一個(gè)網(wǎng)絡(luò)輸入操作通常包括兩個(gè)不同階段:
等待網(wǎng)絡(luò)數(shù)據(jù)到達(dá)網(wǎng)卡→讀取到內(nèi)核緩沖區(qū),數(shù)據(jù)準(zhǔn)備好; 從內(nèi)核緩沖區(qū)復(fù)制數(shù)據(jù)到進(jìn)程空間。
2、5種IO模型
《UNIX網(wǎng)絡(luò)編程》說得很清楚,5種IO模型分別是阻塞IO模型、非阻塞IO模型、IO復(fù)用模型、信號驅(qū)動的IO模型、異步IO模型;前4種為同步IO操作,只有異步IO模型是異步IO操作。
下面這樣些圖,是它里面給出的例子:接收網(wǎng)絡(luò)UDP數(shù)據(jù)的流程在IO模型下的分析,在它的基礎(chǔ)上再加以簡單描述,以區(qū)分這些IO模型。
2-1、阻塞IO模型

進(jìn)程發(fā)起IO系統(tǒng)調(diào)用后,進(jìn)程被阻塞,轉(zhuǎn)到內(nèi)核空間處理,整個(gè)IO處理完畢后返回進(jìn)程。操作成功則進(jìn)程獲取到數(shù)據(jù)。
關(guān)注公眾號程序員小樂回復(fù)關(guān)鍵字“offer”獲取算法面試題和答案。
1、典型應(yīng)用:阻塞socket、Java BIO;
2、特點(diǎn):
進(jìn)程阻塞掛起不消耗CPU資源,及時(shí)響應(yīng)每個(gè)操作;
實(shí)現(xiàn)難度低、開發(fā)應(yīng)用較容易;
適用并發(fā)量小的網(wǎng)絡(luò)應(yīng)用開發(fā);
不適用并發(fā)量大的應(yīng)用:因?yàn)橐粋€(gè)請求IO會阻塞進(jìn)程,所以,得為每請求分配一個(gè)處理進(jìn)程(線程)以及時(shí)響應(yīng),系統(tǒng)開銷大。
2-2、非阻塞IO模型

進(jìn)程發(fā)起IO系統(tǒng)調(diào)用后,如果內(nèi)核緩沖區(qū)沒有數(shù)據(jù),需要到IO設(shè)備中讀取,進(jìn)程返回一個(gè)錯(cuò)誤而不會被阻塞;進(jìn)程發(fā)起IO系統(tǒng)調(diào)用后,如果內(nèi)核緩沖區(qū)有數(shù)據(jù),內(nèi)核就會把數(shù)據(jù)返回進(jìn)程。
對于上面的阻塞IO模型來說,內(nèi)核數(shù)據(jù)沒準(zhǔn)備好需要進(jìn)程阻塞的時(shí)候,就返回一個(gè)錯(cuò)誤,以使得進(jìn)程不被阻塞。
1、典型應(yīng)用:socket是非阻塞的方式(設(shè)置為NONBLOCK)
2、特點(diǎn):
進(jìn)程輪詢(重復(fù))調(diào)用,消耗CPU的資源;
實(shí)現(xiàn)難度低、開發(fā)應(yīng)用相對阻塞IO模式較難;
適用并發(fā)量較小、且不需要及時(shí)響應(yīng)的網(wǎng)絡(luò)應(yīng)用開發(fā);
2-3、IO復(fù)用模型

多個(gè)的進(jìn)程的IO可以注冊到一個(gè)復(fù)用器(select)上,然后用一個(gè)進(jìn)程調(diào)用該select, select會監(jiān)聽所有注冊進(jìn)來的IO;
如果select沒有監(jiān)聽的IO在內(nèi)核緩沖區(qū)都沒有可讀數(shù)據(jù),select調(diào)用進(jìn)程會被阻塞;而當(dāng)任一IO在內(nèi)核緩沖區(qū)中有可數(shù)據(jù)時(shí),select調(diào)用就會返回;
而后select調(diào)用進(jìn)程可以自己或通知另外的進(jìn)程(注冊進(jìn)程)來再次發(fā)起讀取IO,讀取內(nèi)核中準(zhǔn)備好的數(shù)據(jù)。
可以看到,多個(gè)進(jìn)程注冊IO后,只有另一個(gè)select調(diào)用進(jìn)程被阻塞。
1、典型應(yīng)用:select、poll、epoll三種方案,nginx都可以選擇使用這三個(gè)方案;Java NIO;
2、特點(diǎn):
專一進(jìn)程解決多個(gè)進(jìn)程IO的阻塞問題,性能好;Reactor模式;
實(shí)現(xiàn)、開發(fā)應(yīng)用難度較大;
適用高并發(fā)服務(wù)應(yīng)用開發(fā):一個(gè)進(jìn)程(線程)響應(yīng)多個(gè)請求;
3、select、poll、epoll
Linux中IO復(fù)用的實(shí)現(xiàn)方式主要有select、poll和epoll:
Select:注冊IO、阻塞掃描,監(jiān)聽的IO最大連接數(shù)不能多于FD_SIZE;
Poll:原理和Select相似,沒有數(shù)量限制,但I(xiàn)O數(shù)量大掃描線性性能下降;
Epoll :事件驅(qū)動不阻塞,mmap實(shí)現(xiàn)內(nèi)核與用戶空間的消息傳遞,數(shù)量很大,Linux2.6后內(nèi)核支持;
2-4、信號驅(qū)動IO模型

當(dāng)進(jìn)程發(fā)起一個(gè)IO操作,會向內(nèi)核注冊一個(gè)信號處理函數(shù),然后進(jìn)程返回不阻塞;當(dāng)內(nèi)核數(shù)據(jù)就緒時(shí)會發(fā)送一個(gè)信號給進(jìn)程,進(jìn)程便在信號處理函數(shù)中調(diào)用IO讀取數(shù)據(jù)。
特點(diǎn):回調(diào)機(jī)制,實(shí)現(xiàn)、開發(fā)應(yīng)用難度大;
2-5、異步IO模型

當(dāng)進(jìn)程發(fā)起一個(gè)IO操作,進(jìn)程返回(不阻塞),但也不能返回果結(jié);內(nèi)核把整個(gè)IO處理完后,會通知進(jìn)程結(jié)果。如果IO操作成功則進(jìn)程直接獲取到數(shù)據(jù)。
關(guān)注公眾號程序員小樂回復(fù)關(guān)鍵字“Java”獲取Java面試題和答案。
1、典型應(yīng)用:JAVA7 AIO、高性能服務(wù)器應(yīng)用
2、特點(diǎn):
不阻塞,數(shù)據(jù)一步到位;Proactor模式;
需要操作系統(tǒng)的底層支持,LINUX 2.5 版本內(nèi)核首現(xiàn),2.6 版本產(chǎn)品的內(nèi)核標(biāo)準(zhǔn)特性;
實(shí)現(xiàn)、開發(fā)應(yīng)用難度大;
非常適合高性能高并發(fā)應(yīng)用;
3、IO模型比較
3-1、阻塞IO調(diào)用和非阻塞IO調(diào)用、阻塞IO模型和非阻塞IO模型
注意這里的阻塞IO調(diào)用和非阻塞IO調(diào)用不是指阻塞IO模型和非阻塞IO模型:
阻塞IO調(diào)用 :在用戶進(jìn)程(線程)中調(diào)用執(zhí)行的時(shí)候,進(jìn)程會等待該IO操作,而使得其他操作無法執(zhí)行。 非阻塞IO調(diào)用:在用戶進(jìn)程中調(diào)用執(zhí)行的時(shí)候,無論成功與否,該IO操作會立即返回,之后進(jìn)程可以進(jìn)行其他操作(當(dāng)然如果是讀取到數(shù)據(jù),一般就接著進(jìn)行數(shù)據(jù)處理)。
這個(gè)直接理解就好,進(jìn)程(線程)IO調(diào)用會不會阻塞進(jìn)程自己。所以這里兩個(gè)概念是相對調(diào)用進(jìn)程本身狀態(tài)來講的。
從上面對比圖片來說,阻塞IO模型是一個(gè)阻塞IO調(diào)用,而非阻塞IO模型是多個(gè)非阻塞IO調(diào)用+一個(gè)阻塞IO調(diào)用,因?yàn)槎鄠€(gè)IO檢查會立即返回錯(cuò)誤,不會阻塞進(jìn)程。
而上面也說過了,非阻塞IO模型對于阻塞IO模型來說區(qū)別就是,內(nèi)核數(shù)據(jù)沒準(zhǔn)備好需要進(jìn)程阻塞的時(shí)候,就返回一個(gè)錯(cuò)誤,以使得進(jìn)程不被阻塞。
3-2、同步IO和異步IO
同步IO:導(dǎo)致請求進(jìn)程阻塞,直到I/O操作完成。
異步IO:不導(dǎo)致請求進(jìn)程阻塞。
上面兩個(gè)定義是《UNIX網(wǎng)絡(luò)編程 卷1:套接字聯(lián)網(wǎng)API》給出的。這不是很好理解,我們來擴(kuò)展一下,先說說同步和異步,同步和異步關(guān)注的是雙方的消息通信機(jī)制:
同步:雙方的動作是經(jīng)過雙方協(xié)調(diào)的,步調(diào)一致的。 異步:雙方并不需要協(xié)調(diào),都可以隨意進(jìn)行各自的操作。
這里我們的雙方是指,用戶進(jìn)程和IO設(shè)備;明確同步和異步之后,我們在上面網(wǎng)絡(luò)輸入操作例子的基礎(chǔ)上,進(jìn)行擴(kuò)展定義:
同步IO:用戶進(jìn)程發(fā)出IO調(diào)用,去獲取IO設(shè)備數(shù)據(jù),雙方的數(shù)據(jù)要經(jīng)過內(nèi)核緩沖區(qū)同步,完全準(zhǔn)備好后,再復(fù)制返回到用戶進(jìn)程。而復(fù)制返回到用戶進(jìn)程會導(dǎo)致請求進(jìn)程阻塞,直到I/O操作完成。
異步IO:用戶進(jìn)程發(fā)出IO調(diào)用,去獲取IO設(shè)備數(shù)據(jù),并不需要同步,內(nèi)核直接復(fù)制到進(jìn)程,整個(gè)過程不導(dǎo)致請求進(jìn)程阻塞。
所以, 阻塞IO模型、非阻塞IO模型、IO復(fù)用模型、信號驅(qū)動的IO模型者為同步IO模型,只有異步IO模型是異步IO。
來源:https://blog.csdn.net/tjiyu/article/details/52959418
版權(quán)申明:內(nèi)容來源網(wǎng)絡(luò),版權(quán)歸原創(chuàng)者所有。除非無法確認(rèn),我們都會標(biāo)明作者及出處,如有侵權(quán)煩請告知,我們會立即刪除并表示歉意。謝謝!


