java nio io模型

I/O模型html

在開始NIO的學習以前,先對I/O的模型有一個理解,這對NIO的學習是絕對有好處的。我畫一張圖,簡單表示一下數據從外部磁盤向運行中進程的內存區域移動的過程:編程

這張圖片明顯忽略了不少細節,只涉及了基本操做,下面分析一下這張圖。緩存

 

用戶空間和內核空間安全

一個計算機一般有必定大小的內存空間,如一臺計算機有4GB的地址空間,可是程序並不能徹底使用這些地址空間,由於這些地址空間是被劃分爲用戶空間和內核空間的。程序只能使用用戶空間的內存,這裏所說的使用是指程序可以申請的內存空間,並非真正訪問的地址空間。下面看下什麼是用戶空間和內核空間:網絡

一、用戶空間dom

     用戶空間是常規進程所在的區域,什麼是常規進程,打開任務管理器看到的就是常規進程:異步

     

     JVM就是常規進程,駐守於用戶空間,用戶空間是非特權區域,好比在該區域執行的代碼不能直接訪問硬件設備。分佈式

二、內核空間函數

     內核空間主要是指操做系統運行時所使用的用於程序調度、虛擬內存的使用或者鏈接硬件資源等的程序邏輯。內核代碼有特別的權利,好比它能與設備控制器通信,控制着整個用於區域進程的運行狀態。和I/O相關的一點是:全部I/O都直接或間接經過內核空間性能

那麼,爲何要劃分用戶空間和內核空間呢?這也是爲了保證操做系統的穩定性和安全性。用戶程序不能夠直接訪問硬件資源,若是用戶程序須要訪問硬件資源,必須調用操做系統提供的接口,這個調用接口的過程也就是系統調用。每一次系統調用都會存在兩個內存空間之間的相互切換,一般的網絡傳輸也是一次系統調用,經過網絡傳輸的數據先是從內核空間接收到遠程主機的數據,而後再從內核空間複製到用戶空間,供用戶程序使用。這種從內核空間到用戶控件的數據複製很費時,雖然保住了程序運行的安全性和穩定性,可是犧牲了一部分的效率。

最後,如何分配用戶空間和內核空間的比例也是一個問題,是更多地分配給用戶空間供用戶程序使用,仍是首先保住內核有足夠的空間來運行,仍是要平衡一下。在當前的Windows 32位操做系統中,默認用戶空間:內核空間的比例是1:1,而在32位Linux系統中的默認比例是3:1(3GB用戶空間、1GB內核空間)。

 

進程執行I/O操做的步驟

緩衝區,以及緩衝區如何工做,是全部I/O的基礎。所謂"輸入/輸出"講的無非也就是把數據移入或移出緩衝區。

進程執行I/O操做,歸結起來,就是向操做系統發出請求,讓它要麼把緩衝區裏的數據排乾淨(寫),要麼用數據把緩衝區填滿(讀)。進程利用這一機制處理全部數據進出操做,操做系統內部處理這一任務的機制,其複雜程度可能超乎想像,但就概念而言,卻很是直白易懂,從上面的圖,能夠總結一下進程執行I/O操做的幾步:

一、進程使用底層函數read(),創建和執行適當的系統調用,要求其緩衝區被填滿,此時控制權移交給內核

二、內核隨即向磁盤控制硬件發出命令,要求其從磁盤讀取數據

三、磁盤控制器和數據直接寫入內核內存緩衝區,這一步經過DMA完成,無需主CPU協助。這裏多提一句,關於DMA,能夠百度一下,它是現代電腦的重要特點,它容許不一樣速度的硬件裝置來溝通,而不須要依賴於CPU的大量中斷負載,大大提高了整個系統的效率

四、一盤磁盤控制器把緩衝區填滿,內核隨即把數據從內核空間的臨時緩衝區拷貝到進程執行read()調用時指定的緩衝區

五、進程從用戶空間的緩衝區中拿到數據

固然,若是內核空間裏已經有數據了,那麼該數據只須要簡單地拷貝出來便可。至於爲何不能直接讓磁盤控制器把數據送到用戶空間的緩衝區呢?最簡單的一個理由就是,硬件一般不能直接訪問用戶空間。

 

同步和異步、阻塞和非阻塞

有了上面對於I/O的解讀,咱們來看一下同步和異步、阻塞和非阻塞兩組概念的區別,主要兩者在關注點上有所不一樣。

一、同步和異步

同步和異步這個概念比較廣,不只僅是在I/O,其餘的還有諸如同步調用/異步調用、同步請求/異步請求,都是一個意思。同步和異步,關注的是消息通訊機制

所謂同步,就是在發出一個"調用請求"時,在沒有獲得結果以前,該"調用請求"就不返回,可是一旦調用返回就獲得返回值了。換句話說,就是由"調用者"主動等待"調用"的結果。像咱們平時寫的,方法A調用Math.random()方法、方法B調用String.substring()方法都是同步調用,由於調用者主動在等待這些方法的返回。

所謂異步,則正好相反,"調用"發出以後,這個調用就直接返回了,全部沒有返回結果。換句話說,當一個異步調用請求發出以後,調用者不會馬上獲得結果,所以異步調用適用於那些對數據一致性要求不是很高的場景,好比模塊A更新了緩存中的某個值,模塊B將某個內容分享到新浪微博,這些模塊的關注點更可能是"作了這件事"而不是"作了這件事是否立刻成功",用分佈式的話說,就是犧牲了系統的強一致性而提升了整個系統的可用性及分區容錯性。若是這種場景下,咱們但願獲取異步調用的結果,"被調用者"能夠經過狀態、通知來通知調用者,或經過回調函數處理這個調用,對應Java中的有Future/FutureTask、wait/notify。

二、阻塞和非阻塞

阻塞和非阻塞關注的是程序在等待調用結果時的狀態

阻塞調用指的是調用結果返回以前,當前線程會被掛起,調用線程只有在獲得結果以後纔會返回。

非阻塞調用指的是在不能當即獲得結果以前,該調用不會阻塞當前線程。

 

Linux網絡I/O模型

因爲絕大多數的Java應用都部署在Linux系統上,所以這裏談一下Linux網絡I/O模型。

Linux的內核將全部外部設備都看作一個文件來操做,對一個文件的讀寫操做會調用內核提供的系統命令,返回一個file descriptor(fd,文件描述符)。而對一個Socket的讀寫也會有相應的描述符,稱爲Socketfd(Socket描述符),描述符就是一個數字,它指向內核中的一個結構體(結構體,C/C++數據類型,相似Java中的類,存儲各類不一樣類型的數據,這裏存儲的是文件路徑、數據區等一些屬性)。

根據UNIX網絡編程對I/O模型的分類,UNIX提供了5種I/O模型,分別爲:

一、阻塞I/O模型

阻塞I/O模型就是最經常使用的I/O模型,缺省狀況下全部的文件操做都是阻塞的,以Socket來說解此模型:在用戶空間中調用recvfrom,其系統調用直到數據包到達且被複制到應用進程的緩衝區或者發生錯誤時才返回,在此期間會一直等待,進程在從調用recvfrom開始到它返回的整段時間內都是被阻塞的,所以被稱爲阻塞I/O。

二、非阻塞I/O模型

recvfrom從用戶空間到內核空間的時候,若是該緩衝區沒有數據的話,就直接返回一個EWOULDBOCK錯誤,通常都對非阻塞I/O模型進行輪詢檢查這個狀態,看內核空間是否是有數據到來,有數據到來則從內核空間複製數據到用戶空間。

三、I/O複用模型

Linux提供select/poll,進程經過將一個或者多個fd傳遞給select或poll系統調用,阻塞在select操做上,這樣select/poll能夠幫助咱們偵測多個fd是否處於就緒狀態。select/poll是順序掃描fd是否就緒,並且支持的fd數量有限,所以它的使用受到了一些制約。Linux還提供了一個epoll系統調用,epoll使用基於事件驅動方式替代順序掃描,所以性能更高。當有fd就緒時,當即會掉函數rollback。

四、信號驅動I/O模型

首先開啓Socket信號驅動I/O功能,並經過系統調用sigaction執行一個信號處理函數(此係統調用當即返回,進程繼續工做,它是非阻塞的)。當數據準備就緒時,就爲進程生成一個SIGIO信號,經過信號會掉通知應用程序調用recvfrom來讀取數據,並通知主循環函數來處理數據。

五、異步I/O

告知內核啓動某個操做,並讓內核在整個操做完成後(包括將數據從內核複製到用戶本身的緩衝區)通知開發者。這種模型與信號驅動I/O模型的主要區別是:信號驅動I/O模型由內核通知開發者什麼時候能夠開始一個I/O操做,異步I/O模型由內核通知開發者I/O操做什麼時候已經完成

原文:http://www.cnblogs.com/xrq730/p/5074199.html

相關文章
相關標籤/搜索