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