1. 程式人生 > >Netty快速入門(01)Linux I/O模型介紹

Netty快速入門(01)Linux I/O模型介紹

Netty簡述

Netty是一個高效能的網路程式設計框架。

上面提到了幾個關鍵的字眼,高效能,網路程式設計,框架。這些概括Netty的本質。

Netty是一個NIO客戶端伺服器框架,可以快速輕鬆地開發協議伺服器和客戶端等網路應用程式。它極大地簡化並簡化了TCP和UDP套接字伺服器等網路程式設計。

“快速簡便”並不意味著最終的應用程式會受到可維護性或效能問題的影響。Netty經過精心設計,具有豐富的協議,如FTP,SMTP,HTTP以及各種二進位制和基於文字的傳統協議。因此,Netty成功地找到了一種在不妥協的情況下實現易於開發,效能,穩定性和靈活性的方法。

先來簡單看一下Netty官網的架構圖:

從圖中可以看出,Netty的架構分為三塊,核心(Core),通訊服務(Transport Services)還有各種協議(Protocol Support)。我們想象將來要學習的內容都和這些有關係。通過架構圖對其有個基本印象。

Linux I/O 模型

在Linux中,將I/O模型分為五種型別:

  1. 阻塞式 I/O 模型

  2. 非阻塞式 I/O 模型

  3. I/O 複用

  4. 訊號驅動式 I/O

  5. 非同步 I/O

Linux中的I/O流程概括來說可以分為兩步:

1、等待資料準備好(Waiting for the data to be ready)

2、從核心向程序複製資料(Copying the data from the kernel to the process)

來看下面的圖片:

從上面的流程可以看出,首先是核心空間把資料從硬體(磁碟)中讀到核心空間的緩衝區中,進行這一步操作時,資料處於核心態,通過磁碟驅動器,把資料從磁碟讀到核心緩衝區來。下一步是把資料從核心緩衝區拷貝到使用者空間的緩衝區,資料由核心態變為使用者態。

知識普及:Linux中的程式大致執行在兩個空間,自己的程式執行在使用者空間,使用者空間的許可權有限,相對的另外一個空間是核心空間,比如驅動程式或者一些核心的系統呼叫都是在核心空間完成的。我們的I/O操作當在一個使用者空間做系統呼叫的時候,資料會自動從使用者態變到核心態來進行操作,完成了以後再從核心空間拷貝到使用者空間。這種狀態的切換是為了保證安全。比如在我們32位的作業系統中,大部分是4G的記憶體,一般前面一個G主要是核心態使用,後面三個G是使用者態使用。

阻塞式 I/O 模型

阻塞式I/O是我們最常見的一種IO,當我們發起一個IO請求之後,開始進行系統呼叫,資料變為核心態,這兩個階段是一直處於阻塞的。資料開始從磁碟拷貝到核心空間,處理完後再從核心空間拷貝到應用空間,資料變為使用者態。這整個的過程涉及到的所有程式執行緒都是在等待狀態。比如我們Java普通的Socket程式設計,就是這樣一個流程。整個過程中資料沒有完成前,接收方都是出於等待狀態,這就是阻塞式IO模型。明顯可見效率不會高。

非阻塞式 I/O 模型

這種方式的特點是呼叫的時候不會等待(也就是不會阻塞),系統如果沒有資料,就會立刻告訴你現在沒資料,會立刻返回,然後由呼叫方自己去問現在有沒有,如果沒有就下次再問有沒有,相當於不斷去輪詢結果,直到某個時刻被告知有結果了,這個時候資料已經從磁碟拷貝到核心空間了,資料也處於核心態,就差最後從核心態拷貝到使用者空間,但是,從核心空間拷貝到使用者空間,讓資料從核心態變為使用者態,這個過程在非阻塞式 I/O 模型中,也是阻塞的!所以效率也不會太高。

I/O 複用模型

複用的意思是,系統一次去檢視多個io的進度,看哪一個有了結果,對於有結果的,就開始執行下面從核心空間拷貝到使用者空間的操作,這個模型和上面非阻塞式 I/O 模型的區別是,非阻塞式 I/O 模型一次只看一個結果好了沒有,而IO複用模型一次可以檢視多個,一次監控一批系統呼叫好了沒有,這是複用模型的特點。在一定程度上,能提高一些效率。

訊號驅動式 I/O 模型

在Linux系統中,有一種訊號機制,也就是說呼叫方可以註冊一個訊號,當系統呼叫完成之後,可以通知這個訊號,那註冊訊號的人就會知道這個請求已經完成了,這種訊號機制應用在IO當中就是訊號驅動式IO。這樣就不是檢視結果是否好了,而是被通知的一種方式。不過通知後,從核心態到使用者態這個過程還是阻塞的。純非同步的模型就是下面要說的這個。

非同步 I/O模型

這種模型是真正的純非同步的IO模型,當開始系統呼叫,資料從磁碟讀到核心空間,並且從核心態已經拷貝到了使用者態之後,整個流程已經完成了,然後在進行回撥,通知呼叫方。主要的區別在於不是在核心空間中完成了就通知,而是由核心空間到了使用者空間後再通知,沒有任何阻塞的過程,是一種更加徹底的非同步IO。

各種I/O模型之間的比較

阻塞式IO就是發起呼叫之後,就一直阻塞,在等著,直到整個IO完成。在這個過程當中,程序是不能幹任何事情的。都在等待。

非阻塞式IO就是,程序會主動去問好了沒,好了沒,好了沒。。。直到得到回覆好了,然後發起讀請求,把資料從核心空間拷貝到使用者空間。這個過程是阻塞的。

IO複用模型,就是每次都檢視多個結果好了沒,如果發現n個當中有一兩個有了結果,就返回,有結果的這些開始拷貝。

訊號驅動式IO模型,就是等資料拷貝到核心空間再通知,前面這段時間可以做別的事情,等核心空間準備好了,通知,再把資料從核心空間拷貝到使用者空間。

非同步IO模型,就是發起呼叫後就徹底不管了,等資料好了後從核心空間拷貝到使用者空間了,再進行通知,整個過程沒有阻塞的步驟。

從比較重來看,非同步IO從理論上來看是最好的,不存在任何阻塞的時間,系統資源理論上可以得到充分利用。

同步VS非同步

lPOSIX標準將同步I/O和非同步I/O定義為:

同步I/O操作:導致請求程序阻塞,直到I/O操作完成。

非同步I/O操作:不導致請求程序阻塞。

從定義上看,只有最後的非同步IO模型是真正的非同步,前面四種都達不到真正的非同步。但是目前來看,純非同步的IO一直都不太成熟,比較混亂,沒有一個標準的解決方案。用的最多的還是IO複用模型,這個是目前為止比較成熟的一個模型。

在Java中,我們用的最多的Socket網路程式設計,就是阻塞式IO模型,後來的NIO就是IO複用模型,在Java中不支援訊號驅動模型。Java的NIO2.0,也叫AIO,提供了純非同步的IO模型,但是現在的非同步IO模型還不是很成熟,所以用的最多的就是NIO,也就是IO複用模型。

推薦書籍《Unix網路程式設計》,推薦理由:網路程式設計的經典之作,經歷了十年左右,內容基本變化不大,原理性書籍,是網路程式設計的必看之書。

本文由部落格一文多發平臺 OpenWrite 釋出!