Unix中的I/O模型和Java NIO
Unix網(wǎng)絡(luò)編程中的五種I/O模型
阻塞式I/0、非阻塞I/O、I/O復(fù)用、信號(hào)驅(qū)動(dòng)式I/O、異步I/O
以一個(gè)網(wǎng)絡(luò)輸入為例,一個(gè)輸入操作通常包括兩個(gè)不同的階段:
等待數(shù)據(jù)準(zhǔn)備好,將數(shù)據(jù)從內(nèi)核拷貝到進(jìn)程中。具體過程參考下面的圖。
阻塞式I/O
默認(rèn)情況下的I/O模型都是阻塞式I/O,應(yīng)用進(jìn)程從調(diào)用recvfrom開始到它返回的整個(gè)時(shí)間段內(nèi)都是被阻塞的。此也就是說,應(yīng)用程序進(jìn)程投入睡眠,內(nèi)核在檢查到有數(shù)據(jù)準(zhǔn)備好之后,將數(shù)據(jù)從內(nèi)核復(fù)制到進(jìn)程后才返回。
非阻塞I/O
將套接字設(shè)置為非阻塞會(huì)通知內(nèi)核:當(dāng)所請(qǐng)求的I/O操作要把本進(jìn)程投入睡眠才能完成時(shí),不要投入睡眠,而是返回一個(gè)錯(cuò)誤。如圖,當(dāng)用戶進(jìn)程讀取數(shù)據(jù)時(shí)而內(nèi)核沒有可讀數(shù)據(jù)時(shí),用戶進(jìn)程并沒有阻塞,而是立刻返回EWOULDBLOCK,這樣通過輪詢返回值,當(dāng)沒有數(shù)據(jù)準(zhǔn)備好,就繼續(xù)調(diào)用recvfrom,當(dāng)內(nèi)核準(zhǔn)備好了數(shù)據(jù),并且再次收到請(qǐng)求數(shù)據(jù)的系統(tǒng)調(diào)用時(shí),就會(huì)將數(shù)據(jù)復(fù)制到進(jìn)程。
I/O復(fù)用
阻塞在select或者poll這兩個(gè)系統(tǒng)調(diào)用中的某一個(gè)之上,而不是阻塞在真正的I/O系統(tǒng)調(diào)用之上。
從圖中可以看出,用戶進(jìn)程阻塞在select之上,等到有可讀的套接字,就可以調(diào)用recvfrom將數(shù)據(jù)復(fù)制到進(jìn)程緩沖區(qū)。
和阻塞式比較起來,I/O復(fù)用并沒有什么優(yōu)勢(shì),相反還優(yōu)劣勢(shì),因?yàn)樯婕暗絻纱蜗到y(tǒng)調(diào)用,然而使用select的優(yōu)勢(shì)在于可以單線程管理多個(gè)套接字(描述符)連接。
傳統(tǒng)的監(jiān)控多個(gè)socket的解決方案是為每個(gè)socket創(chuàng)建一個(gè)線程,并使得線程可以再read調(diào)用中阻塞,直到數(shù)據(jù)可用。這事實(shí)上將每個(gè)被阻塞的線程當(dāng)作了socket監(jiān)控器,并將Java 虛擬機(jī)的線程調(diào)度當(dāng)作了通知機(jī)制。這兩者本來都不是為了這種目的而設(shè)計(jì)的。程序員和Java 虛擬機(jī)都為管理所有這些線程的復(fù)雜性和性能損耗付出了代價(jià),這在線程數(shù)量的增長(zhǎng)失控時(shí)表現(xiàn)得更為突出。由于開啟多個(gè)會(huì)造成資源的大量浪費(fèi)。由于網(wǎng)絡(luò)延遲的原因,同時(shí)在處理socket的用戶線程往往比實(shí)際的socket數(shù)量要少很多,然而由于線程數(shù)量太多,cpu來回切換卻要花費(fèi)過多的資源。
Java 1.4中首次加入NIO,就是一種I/O復(fù)用,使用的selector選擇器。
信號(hào)驅(qū)動(dòng)式I/O
使用信號(hào),讓內(nèi)核在socket就緒時(shí)發(fā)送SIGIO信號(hào)通知我們。開啟套接字的信號(hào)驅(qū)動(dòng)式I/O功能。并通過sigaction系統(tǒng)調(diào)用安裝一個(gè)信號(hào)處理函數(shù)(回調(diào)函數(shù)),該系統(tǒng)調(diào)用立刻返回。隨后就可以在信號(hào)處理函數(shù)中調(diào)用recvfrom讀取到數(shù)據(jù)。
異步I/O
當(dāng)用戶進(jìn)程調(diào)用read內(nèi)核即使沒有數(shù)據(jù)準(zhǔn)備好也會(huì)立刻返回,并不會(huì)阻塞用戶進(jìn)程,然后內(nèi)核在整個(gè)操作完成后再通知進(jìn)程,通過回調(diào)函數(shù)通知。這里的整個(gè)操作包括等待數(shù)據(jù)準(zhǔn)備好和將數(shù)據(jù)從內(nèi)核復(fù)制到用戶進(jìn)程的緩沖區(qū)。
和信號(hào)驅(qū)動(dòng)式I/O的區(qū)別是:號(hào)驅(qū)動(dòng)式I/O告訴我們什么時(shí)候可以啟動(dòng)一個(gè)I/O操作,而異步I/O中內(nèi)核通知我們I/O操作何時(shí)完成,就是說等待數(shù)據(jù)準(zhǔn)備好和將數(shù)據(jù)復(fù)制到進(jìn)程都是內(nèi)核做的,直到這兩步完成后直接通知用戶進(jìn)程拿數(shù)據(jù)。
Java在jdk1.7中加入了異步I/O叫做AIO
五種I/O模型的區(qū)別
阻塞和非阻塞
阻塞和非阻塞的區(qū)別在有沒有I/O調(diào)用所導(dǎo)致的由于數(shù)據(jù)未準(zhǔn)備好造成的進(jìn)程睡眠。如果一個(gè)I/O操作的系統(tǒng)調(diào)用發(fā)出后,需要讓當(dāng)前進(jìn)程睡眠,就是阻塞式的,否則就是非阻塞式的。
同步和異步
同步I/O:導(dǎo)致請(qǐng)求進(jìn)程阻塞直到I/O操作完成,也就是說在數(shù)據(jù)從內(nèi)核復(fù)制到進(jìn)程完成之前,進(jìn)程一直阻塞。
異步I/O:不導(dǎo)致請(qǐng)求進(jìn)程阻塞。
這樣看來,前四種都屬于同步I/O,異步就是異步,沒有阻塞和非阻塞的概念。
Java中的I/O模型
Bio:阻塞式I/O
Nio:非阻塞I/O(jdk 1.4)
Aio:異步I/O(jdk 1.7)
NIO是什么?
Java在1.4中首次增加了NIO(New I/O,Non-blockingI/O),是一種同步非阻塞的I/O模型。引入NIO的目的在于提高速度,在此基礎(chǔ)上,以前版本中舊的I/O操作的函數(shù)全部改寫為使用NIO時(shí)實(shí)現(xiàn),因此即使不顯式的使用NIO編寫代碼,也能從中受益,速度的提高包括文件I/O和網(wǎng)絡(luò)I/O,網(wǎng)絡(luò)I/O中最大的收益在于可以使用單線程來管理多個(gè)Socket連接,是一種I/O復(fù)用。
為什么?
JVM自身在I/O 方面效率欠佳。操作系統(tǒng)與Java基于流的I/O模型有些不匹配。操作系統(tǒng)要移動(dòng)的是大塊數(shù)據(jù)(緩沖區(qū)),這往往是在硬件直接存儲(chǔ)器存取(DMA)的協(xié)助下完成的。而JVM的I/O 類喜歡操作小塊數(shù)據(jù)——單個(gè)字節(jié)、幾行文本。結(jié)果,操作系統(tǒng)送來整緩沖區(qū)的數(shù)據(jù),java.io的流數(shù)據(jù)類再花大量時(shí)間把它們拆成小塊,往往拷貝一個(gè)小塊就要往返于幾層對(duì)象。操作系統(tǒng)喜歡整卡車地運(yùn)來數(shù)據(jù),java.io類則喜歡一鏟子一鏟子地加工數(shù)據(jù)。有了NIO,就可以輕松地把一卡車數(shù)據(jù)備份到您能直接使用的地方(ByteBuffer對(duì)象)---- Java Nio O'Reilly Media
傳統(tǒng)流I/O是基于字節(jié)的,所有I/O都被視為單個(gè)字節(jié)的移動(dòng);而NIO是基于塊的,大家可能猜到了,NIO的性能肯定優(yōu)于流I/O。沒錯(cuò)!其性能的提高 要得益于其使用的結(jié)構(gòu)更接近操作系統(tǒng)執(zhí)行I/O的方式:通道和緩沖器。我們可以把它想象成一個(gè)煤礦,通道是一個(gè)包含煤層(數(shù)據(jù))的礦藏,而緩沖器則是派送到礦藏的卡車。卡車載滿煤炭而歸,我們?cè)購目ㄜ嚿汐@得煤炭。也就是說,我們并沒有直接和通道交互;我們只是和緩沖器交互,并把緩沖器派送到通道。通道要么從緩沖器獲得數(shù)據(jù),要么向緩沖器發(fā)送數(shù)據(jù)----Thinking in java
注:這是一篇筆記,包括自己的思考總結(jié),所有內(nèi)容均來自
《Unix網(wǎng)絡(luò)編程》
《Java Nio》
《Thinking injava》
轉(zhuǎn)載于:https://www.cnblogs.com/qhyuan1992/p/5385274.html
總結(jié)
以上是生活随笔為你收集整理的Unix中的I/O模型和Java NIO的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 特征权重量化 TF-IDF 用于信息
- 下一篇: jsp页面整体无法居中问题的解决方案