1. 程式人生 > >Netty學習之路(一)- 同步與非同步IO

Netty學習之路(一)- 同步與非同步IO

本篇部落格主要是講一些基礎,記錄我的學習過程,同時嘗試養成寫部落格的習慣。內容基本來自Netty權威指南加上一丟丟的個人理解。。。。

I/O基礎入門

在jdk1.4以前,java對i/o的支援並不完善,開發人員在開發高效能i/o時會遇到巨大的挑戰與困難,主要問題如下:

  • 沒有資料快取區,i/o效能存在問題
  • 沒有c或c++中Channel概念,只有輸入輸出流
  • 同步阻塞I/O(BIO)會導致通訊執行緒長時間阻塞
  • 支援的字符集有限,硬體移植能力差

在java支援非同步i/o之前,高效能服務端開發領域一直被c和c++佔據,java同步i/o一直
被大家所詬病。

Linux網路I/O模型簡介

linux將所有外部裝置都看成檔案來操作,無論是對檔案還是套接字(socket)的讀寫都會返回一個描述符(指向核心中的一個結構體,結構體包括檔案路徑,資料區等屬性)。應用程式要為因特網通訊而建立一個套接字(socket)時,作業系統就返回一個小整數作為描述符(descriptor)來標識這個套接字。然後,應用程式以該描述符作為傳遞引數,通過呼叫函式來完成某種操作(例如通過網路傳送資料或接收輸入的資料)。

當應用程式要建立一個套接字時,作業系統就返回一個小整數作為描述符,應用程式則使用這個描述符來引用該套接字,然後需要I/O請求的應用程式請求作業系統開啟一個檔案。作業系統就建立一個檔案描述符提供給應用程式訪問檔案。從應用程式的角度看,檔案描述符是一個整數,應用程式可以用它來讀寫檔案

根據unix網路程式設計對I/O模型的分類,linux提供了5種I/O模型:

  1. 阻塞I/O模型:應用程序被阻塞,直到資料複製到應用程序緩衝區中才返回。應該注意到,在阻塞的過程中,其它程式還可以執行,因此阻塞不意味著整個作業系統都被阻塞。因為其他程式還可以執行,所以不消耗 CPU 時間,這種模型的 CPU 利用率效率會比較高。
  2. 非阻塞I/O:應用程序執行系統呼叫之後,核心返回一個錯誤碼。應用程序可以繼續執行,但是需要不斷的執行系統呼叫來獲知 I/O 是否完成,這種方式稱為輪詢(polling)。由於 CPU 要處理更多的系統呼叫,因此這種模型的 CPU 利用率是比較低的。
  3. I/O複用:使用 select 或者 poll 等待資料,並且可以等待多個套接字中的任何一個變為可讀。這一過程會被阻塞,當某一個套接字可讀時返回,之後再使用 recvfrom 把資料從核心複製到程序中。它可以讓單個程序具有處理多個 I/O 事件的能力。又被稱為 Event Driven I/O,即事件驅動 I/O。如果一個 Web 伺服器沒有 I/O 複用,那麼每一個 Socket 連線都需要建立一個執行緒去處理。如果同時有幾萬個連線,那麼就需要建立相同數量的執行緒。相比於多程序和多執行緒技術,I/O 複用不需要程序執行緒建立和切換的開銷,系統開銷更小。
  4. 訊號驅動I/O模型:應用程序使用 sigaction 系統呼叫,核心立即返回,應用程序可以繼續執行,也就是說等待資料階段應用程序是非阻塞的。核心在資料到達時嚮應用程序傳送 SIGIO 訊號,應用程序收到之後在訊號處理程式中呼叫 recvfrom 將資料從核心複製到應用程序中。相比於非阻塞式 I/O 的輪詢方式,訊號驅動 I/O 的 CPU 利用率更高。
  5. 非同步I/O:應用程序執行 aio_read 系統呼叫會立即返回,應用程序可以繼續執行,不會被阻塞,核心會在所有操作完成之後嚮應用程序傳送訊號。非同步 I/O 與訊號驅動 I/O 的區別在於,非同步 I/O 的訊號是通知應用程序 I/O 完成,而訊號驅動 I/O 的訊號是通知應用程序可以開始 I/O。