Linux下實現的IO模型:java
Linux的內核將全部外部設備都看作一個文件來操做,對一個文件的讀寫操做會調用內核提供的系統命令,返回一個file descriptor(fd,文件描述符)。對一個socket編程
的讀寫也會有相應的描述符,稱爲socket描述符。描述符就是一個數字,它執行內核中的一個結構體(文件路徑,數據區等一些屬性)。服務器
由於程序運行在操做系統上,編程語言實現的IO操做API最終依賴於操做系統的IO實現。先理清阻塞、非阻塞、同步、異步這幾個概念:網絡
阻塞:調用方發起調用請求,在沒有返回結果以前,調用方線程被掛起,出於一直等待狀態多線程
非阻塞:與阻塞相對,調用方發起調用請求,當前線程不會等待掛起,而會當即返回。後續能夠經過輪詢等手段來獲取調用結果狀態。併發
同步:所謂同步,就是在發出一個功能調用時,在沒有獲得結果以前,該調用就不返回異步
異步:與同步相對,當一個異步過程調用發出後,調用者不會馬上獲得結果,經過回調等措施來處理這個調用。socket
Linux實現了5中IO模型編程語言
一、阻塞IO模型函數
默認狀況下,全部的文件操做都是阻塞的,在進程空間中調用recvform(recvform函數,用於從Socket套接口上接收數據),其系統調用直到數據包到達且被
複製到應用進程的緩衝區中或者發生錯誤才返回,在此期間會一直等待,進程在從調用recvform開始到它返回的整段時間內都是被阻塞的。
二、非阻塞IO模型
recvform從應用層到內核的時候,若是該緩衝區沒有數據的話,就直接返回一個EWOULDBLOCK錯誤,通常都是對非阻塞IO模型進行輪詢來檢查這個狀態,看
內核是否是有數據到來。
三、IO複用模型
Linux提供select/poll,進程經過將一個或多個fd傳遞給select或者poll系統調用,阻塞在select操做上,這樣select/poll能夠幫咱們偵測多個fd是否處於就緒狀態。
select/poll是順序掃描fd是否就緒,並且支持的fd數量有限,所以它的使用受到了一些制約。Linux還提供了一個epoll系統調用,epoll使用基於事件驅動的方式
代替順序掃描,所以性能更高。當有fd就緒時,當即回調函數rollback。
(Java核心類庫Selector就是基於epoll的多路複用技術實現)
四、信號驅動IO模型
首先開啓套接口信號驅動IO功能,並經過系統調用sigaction執行一個信號處理函數(此係統調用當即返回,進程繼續工做,它是非阻塞的)。當數據準備就緒時,
就爲該進程生成一個SIGIO信號,經過信號回調通知應用程序調用recvform來讀取數據,並通知主循環函數處理數據。(好了告訴我,我來處理,我先去忙)
五、異步IO
告知內核啓動某個操做,並讓內核在整個操做完成後(包括將數據從內核複製到用戶本身的緩衝區)通知咱們。與信號驅動模型的主要區別是:信號驅動IO是由內
核通知咱們什麼時候能夠開始一個IO操做;異步IO模型由內核通知咱們什麼時候已經完成。(等處理好了再告訴我)
IO多路複用技術:在IO編程過程當中,當須要同時處理多個客戶端接入請求時,能夠利用多線程或者IO多路複用技術進行處理。IO多路複用技術經過把多個IO的阻塞到
同一個select的阻塞上,從而可使系統在單線程的狀況下同時處理多個客戶端請求。並且與傳統的多線程模型比,最大的優點是開銷小,系統不須要建立新的額
外進程或這線程,也不須要維護這些進程和線程的運行,下降了系統的維護工做量,節省了系統資源。
Java的IO演進
在Java1.4推出NIO以前,基於Java的全部Socket通訊都採用了同步阻塞模式(BIO),一請求一應答,當併發訪問量增大時響應時間延遲也增大,在性能和可靠性方面
存在巨大的瓶頸。所以推出了NIO(Non-Blocked IO)
可是它仍有不少不完善的地方,特別是對文件系統的處理能力仍顯不足,主要問題以下
1)、沒有統一的文件屬性
2)、API能力比較弱,例如目錄的級聯建立和遞歸遍歷,每每須要本身實現
3)、底層存儲系統的一些高級API沒法使用
4)、全部的文件操做都是同步阻塞調用,不支持異步文件讀寫操做
傳統的BIO編程
網絡編程的基本模型是CS模型,也就是兩個進程之間相互通訊,其中服務端提供位置信息(綁定的IP地址和監聽端口),客戶端經過鏈接操做向服務端監聽的地址發起
鏈接請求,經過三次握手創建鏈接,若是鏈接創建成功,雙方就能夠經過網絡套接字(Socket)進行通訊。
在基於傳統同步阻塞模型開發中,ServerSocket負責綁定IP地址,啓動監聽端口,Socket負責發起鏈接操做。鏈接成功以後,雙方經過輸入和輸出流進行同步阻塞式通訊。
ServerSocket的accept方法在鏈接傳入前會一直阻塞。不過BIO主要的問題在於每當有一個新的客戶端請求接入時,服務端必須建立一個新的線程處理新接入的客戶端鏈
路,一個線程只能處理一個客戶端鏈接。處理完成以後,經過輸出流返回應答給客戶端,線程銷燬。在高性能服務器應用領域,每每須要成千上萬個客戶端的併發鏈接,
這種模型顯然沒法知足高性能、高併發接入的場景。
NIO編程
AIO編程
NIO2.0引入了新的異步通道的概念,並提供了異步文件通道和異步套接字通道的實現。異步通道提供如下兩種方式獲取操做結果:
1):經過java.util.concurrent.Future類來表示異步操做的結果
2):在執行異步操做的時候傳入一個java.nio.channels
CompletionHandler接口的實現類做爲操做完成的回調。
NIO2.0的異步套接字通道是真正的異步非阻塞IO,對應於UNIX網絡編程中的事件驅動IO(AIO)。它不須要經過多路複用器對註冊的通道進行輪詢操做便可實現異步讀
寫,從而簡化了NIO的編程模型。