【IO】【Nio和IO內存交互圖】

d:文件描述符的意思

 

需要了解幾個概念:

 

文件描述符:也就是圖上的fd,暫且可以理解爲一個文件描述符對應一個socket,例如同一主機多個客戶端訪問同一個端口號的服務端,由於他們都來於同一個ip地址,也就是tcp的數據包,來自同一個地方,那麼服務端是如何區分這些不同不通的socket呢?就是通過文件描述符來確定,這樣 纔可以保證數據的正確性。

在傳遞文件描述符的時候,會傳遞相應的操作類型,如讀,寫,接受等等,拿NIO來說就是參數SelectionKey. OP_ACCEPT等等類型。

 

文件描述符的個數:

select模型默認的fd個數的是2048個,因此對於那些只是上萬連接的服務器個數就顯然會很少。主要FD的個數是針對於某一個進程而言的,因此,如果想滿足上萬的請求,可以多啓動幾個進程,但是創建進程的效率損耗也是很大的。不過Linux的Poll模型就有個數的顯示,他的個數是最大可以打開文件的個數限制。而NIO就是基於poll模型實現的。

 

圖1:作爲和IO設備數據交流的最底層,操作系統調用相應的驅動程序,來進行IO數據的讀取和寫入,他有兩個特點:

 

第一:可以讀取一個或者多個字節的數據,譬如普通IO數據讀取,就是調用操作系統相應普通IO的API,然後API直接操作這塊功能,bufferInputstream就是讀取多個字節的情況;

第二:有數據返回數據,沒有數據則阻塞IO,準確的說,操作系統調用這塊功能阻塞或者返回數據,繼續推導出客戶端程序阻塞或者返回數據,這也是阻塞IO之所以阻塞的深層次原因;

第三:抽象理解,這裏面會有各種文件描述,所有客戶端的IO操作都針對於文件描述來完成。

 

圖2:操作系統select功能輪詢的目標內存,這塊內存存儲着客戶端需要被監控的(從客戶端傳遞過來)文件描述符,因此,需要監控哪些socket是由客戶端傳遞哪些文件描述符來確定的。內核的select功能會輪詢這塊內存對應的IO(圖1),然後可以知道哪些IO已經有數據可以讀取或者寫入,然後select功能服務會把這些數據複製到圖3,併產生相應的事件,當客戶端調用selector.select()函數時候,可以理解爲輪詢圖2,查看是否有已經準備好的事件,如果沒有任何事件,那麼就會阻塞。因此,一旦select產生了事件,就會是阻塞狀態的客戶端進程返回,說明此時可以進行數據的讀取了。

 

圖3:客戶端真正讀取數據的目標數據塊,這塊內存應該在 操作系統IO內核進程的某一塊內存。當客戶端讀取數據時候,就是直接操作這塊內存,而不是像普通IO那樣直接操作圖1,這塊數據內存的特點是,有數據了直接返回數據,沒有數據直接返回空,而不是阻塞用戶線程。這也是爲什麼NIO是非阻塞類型的原因。

 

圖4:我們最熟悉的客戶端,也即是java應用程序。需要注意的是任何的IO操作包括socket操作,客戶端每進行一個操作,必須要傳遞一個文件描述符對象。然後,這個對象被操作系統獲得後,進行一系列的操作(如放到圖2裏面進行輪詢),貫穿自始到終的IO數據交互。

 

管道:channel,客戶端對於一個文件描述符的操作接口,即圖4到圖3的某一個文件描述符的操作,就叫做一個管道,因此我們讀取數據時候,常通過一個管道對象來進行數據讀取,如socketChnanel.read()

 

 

 

select/poll模型和epoll模型的區別以及epoll模型的引入

 

          NIO是基於OS內核的select/pool模型來進行實現的,如圖2所描述,OS會輪詢fd的集合,來確定那些fd已經處於就緒狀態,問題來了,如果這個fd的集合特別大,任意時間只有部分的socket是活躍的,但是select/poll模型都會線性掃描全部的集合,導致效率直線下降。我們有沒有其他的機制讓效率更高效呢?可以這樣思考,對於select模型,當某個文件描述符就緒時候,他是主動去線性的輪詢並檢測文件描述符,試想,如果文件描述符在自己準備就緒的情況下要是能主動的觸發一些操作,不就可以避免select全面掃描select集合的弊端了嗎?所以偉大的科學家門就發明了這樣一種IO模型,即epoll模型。

     epoll模型時基於callback函數實現的。他只對活躍的socket進行操作,換句話說就是那些socket對應的fd已經處於就緒狀態,由於epoll模型的每個fd都有一個回調函數,這樣fd一旦就緒(本質上是TCP緩衝區有數據),就會調用這個回調函數,進而通知客戶端,這樣就可以避免對不活躍的socket進行輪詢檢測的弊端了。這些功能都是內核幫助實現的,所以也可以成爲一個“僞”AIO。需要注意的是,由於有回調函數的異步操作,所以效率比select還是稍微慢一點,因此,如果所有的socket都是活躍的,如一個高速的lan環境,此時epoll並不比selec效率高很多,但是一旦有空閒的連接如WAN環境,epoll的效率就會很高。

 

 

NIO和IO區別分析(http://ifeve.com/java-nio-vs-io/)

 

面向流和麪向緩衝

 

IO面向流是因爲他直接操作圖1,調用相應的IO接口從底層讀取數據,

Nio面向緩衝,是因爲NIO直接操作的是圖3

 

阻塞和非阻塞

 

IO阻塞 是因爲直接操作圖1,有數據返回,無數據阻塞

NIO非阻塞  是因爲直接 操作圖3,有數據返回,無數據返回空

 

有無選擇器

 

Nio之所以擁有以上兩個特區別,正是因爲他擁有了選擇器,其實準確的說,應該是調用了操作系統底層的select函數模型,從而利用操作系統的select功能來實現面向緩衝區的無阻塞IO操作。

 

影響應用程序設計

 

 

服務端都在單線程的環境下比較:

由於阻塞IO當某個請求沒數據時候,服務端這個線程將會阻塞,不能處理其他請求,而NIO如果客戶端沒有數據,讓就可以立馬返回,進而處理其他的連接。當然,阻塞IO常用的做法是單個線程接收請求,然後每一個請求開闢一個線程來處理,但是如果請求特別多,那麼,服務端開闢過多的線程反而會出現性能的下降。因此,NIO的優勢在於服務端可以用很少的線程來處理大量的併發連接。

對於阻塞IO,如果客戶端的連接數不是太多,且是長連接,一次發送大量的數據,反而阻塞IO會獲得更好的性能優勢,因爲一個線程對應一個SOCKET請求,那麼他就會專門用於處理這個請求。

 

 

 

總結:

阻塞IO如果某一個客戶端沒有數據可以讀寫,那麼服務端的線程就會阻塞,可以試想,有沒有什麼方法可以實現當有數據的時候我應用程序的服務端來讀取數據,沒有數據的時候我服務端線程可以做其他的事情,而不是處於阻塞狀態,這樣可以大大提高服務端的使用率。在這樣一種背景下,操作系統提出了select功能,這個功能來完成所有客戶端需要監控的socket(文件描述符),然後專門構造一塊數據內存,來存放IO接口過來的數據, 這樣讓客戶端操作這塊內存,就可以實現有數據讀取,無數據返回的效果。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章