Netty詳解(二)Linux 網絡IO模型

1. Linux I/O基礎知識

針對linux操作系統而言,將最高的1G字節(從虛擬地址0xC0000000到0xFFFFFFFF),供內核使用,稱爲內核空間,而將較低的3G字節(從虛擬地址0x00000000到0xBFFFFFFF),供各個進程使用,稱爲用戶空間。

在這裏插入圖片描述

有了用戶空間和內核空間,整個linux內部結構可以分爲三部分,從最底層到最上層依次是:硬件–>內核空間–>用戶空間。我們都知道,爲了OS的安全性等的考慮,進程是無法直接操作I/O設備的,其必須通過系統調用請求內核來協助完成I/O動作,而內核會爲每個I/O設備維護一個buffer。

在這裏插入圖片描述

整個請求過程爲: 用戶進程發起請求,內核接受到請求後,從I/O設備中獲取數據到buffer中,再將buffer中的數據copy到用戶進程的地址空間,該用戶進程獲取到數據後再響應客戶端。

2. Linux 網絡I/O模型

在整個請求過程中,I/O設備數據輸入至內核buffer需要時間,而從內核buffer複製數據至進程Buffer也需要時間。因此根據在這兩段時間內等待方式的不同,I/O動作可以分爲以下五種模式:

  • 阻塞I/O (Blocking I/O)
  • 非阻塞I/O (Non-Blocking I/O)
  • I/O複用(I/O Multiplexing)
  • 信號驅動的I/O (Signal Driven I/O)
  • 異步I/O (Asynchrnous I/O)

2.1 阻塞I/O (Blocking I/O)

在這裏插入圖片描述

當用戶進程調用了recvfrom這個系統調用,內核就開始了IO的第一個階段:等待數據準備。對於network io來說,很多時候數據在一開始還沒有到達(比如,還沒有收到一個完整的UDP包),這個時候內核就要等待足夠的數據到來。而在用戶進程這邊,整個進程會被阻塞。當內核一直等到數據準備好了,它就會將數據從內核中拷貝到用戶內存,然後內核返回結果,用戶進程才解除block的狀態,重新運行起來。 所以,blocking IO的特點就是在IO執行的兩個階段都被block了。(整個過程一直是阻塞的)

2.2 非阻塞I/O (Non-Blocking I/O)

linux下,可以通過設置socket使其變爲non-blocking。當對一個non-blocking socket執行讀操作時,流程是這個樣子:

在這裏插入圖片描述

當用戶進程調用recvfrom時,系統不會阻塞用戶進程,而是立刻返回一個ewouldblock錯誤,從用戶進程角度講 ,並不需要等待,而是馬上就得到了一個結果(這個結果就是ewouldblock )。用戶進程判斷標誌是ewouldblock時,就知道數據還沒準備好,於是它就可以去做其他的事了,於是它可以再次發送recvfrom,一旦內核中的數據準備好了。並且又再次收到了用戶進程的system call,那麼它馬上就將數據拷貝到了用戶內存,然後返回。 當一個應用程序在一個循環裏對一個非阻塞調用recvfrom,我們稱爲輪詢。應用程序不斷輪詢內核,看看是否已經準備好了某些操作。這通常是浪費CPU時間。

2.3 I/O複用(I/O Multiplexing)

我們都知道,select/epoll的好處就在於單個process就可以同時處理多個網絡連接的IO。它的基本原理就是select/epoll這個function會不斷的輪詢所負責的所有socket,當某個socket有數據到達了,就通知用戶進程。它的流程如圖:

在這裏插入圖片描述

Linux提供select/epoll,進程通過將一個或者多個fd傳遞給select或者poll系統調用,阻塞在select操作上,這樣select/poll可以幫我們偵測多個fd是否處於就緒狀態。select/poll是順序掃描fd是否就緒,而且支持的fd數量有限,因此它的使用受到一定的限制。Linux還提供了一個epoll系統調用,epoll使用基於事件驅動的方式代替順序掃描,因此性能更高一些。

I/O複用模型具體流程:用戶進程調用了select,那麼整個進程會被block,而同時,內核會“監視”所有select負責的socket,當任何一個socket中的數據準備好了,select就會返回。這個時候用戶進程再調用read操作,將數據從內核拷貝到用戶進程。 這個圖和blocking IO的圖其實並沒有太大的不同,事實上,還更差一些。因爲這裏需要使用兩個
system call (select 和 recvfrom),而blocking IO只調用了一個system call (recvfrom)。但是,用select的優勢在於它可以同時處理多個connection。

2.4 信號驅動的I/O (Signal Driven I/O)

首先用戶進程建立SIGIO信號處理程序,並通過系統調用sigaction執行一個信號處理函數,這時用戶進程便可以做其他的事了,一旦數據準備好,系統便爲該進程生成一個SIGIO信號,去通知它數據已經準備好了,於是用戶進程便調用recvfrom把數據從內核拷貝出來,並返回結果。

在這裏插入圖片描述

2.5 異步I/O

一般來說,這些函數通過告訴內核啓動操作並在整個操作(包括內核的數據到緩衝區的副本)完成時通知我們。這個模型和前面的信號驅動I/O模型的主要區別是,在信號驅動的I/O中,內核告訴我們何時可以啓動I/O操作,但是異步I/O時,內核告訴我們何時I/O操作完成。

在這裏插入圖片描述

當用戶進程向內核發起某個操作後,會立刻得到返回,並把所有的任務都交給內核去完成(包括將數據從內核拷貝到用戶自己的緩衝區),內核完成之後,只需返回一個信號告訴用戶進程已經完成就可以了。

3. 五種Linux I/O網絡模型對比

在這裏插入圖片描述
結果表明:前四個模型之間的主要區別是第一階段,四個模型的第二階段是一樣的:過程受阻在調用recvfrom當數據從內核拷貝到用戶緩衝區。然而,異步I/O處理兩個階段,與前四個不同。

從同步、異步,以及阻塞、非阻塞兩個維度來劃分來看

在這裏插入圖片描述

4. I/O多路複用技術

在I/O編程中,當需要同時處理多個客戶端接入請求的時候,可以利用多線程或者I/O多路複用技術來進行處理。I/O多路複用技術是通過I/O的阻塞複用到同一個select的阻塞上,從而使得系統在單線程的情況下可以同時處理多個客戶端請求。

I/O多路複用技術的優勢:系統開銷小,系統不需要創建新的額外進程或者線程,而不需要維護這些進程和線程的運行,降低了系統的維護工作量,節省了系統資源,I/O多路複用技術的應用場景如下:

  • 服務器需要同時處理多個處於監聽狀態或者多個連接狀態的套接字
  • 服務器需要同時處理多種網絡協議的套接字

目前支持的I/O多路複用的系統調用有select、pselect、poll、epoll,爲了克服select的缺點,epoll作了很多重大的改進:

  • 支持一個進程打開的socket描述符(FD)不受限制(僅受限於操作系統地最大文件句柄數)
  • I/O 效率不會隨着FD的增加而直線下降
  • 使用mmap加速內核與用戶空間的消息傳遞
  • epoll的API更加簡單

5. 四種I/O的對比

5.1 同步非阻塞I/O

在早期的JDK1.4和1.5update10版本之前,JDK的Selector基於select/poll模型實現,它是基於I/O複用技術的非阻塞I/O,不是異步I/O。在JDK1.5 update10和Linux2.6以上版本,Sun優化了Selector的實現,它在底層使用了epoll替換了select/poll,但是上層的API並沒有變化。所以NIO還是一種同步非阻塞I/O

5.2 異步非阻塞I/O

在JDK1.7提供的AIO新增了異步的套接字通道,它是真正的異步I/O,在異步I/O操作的時候可以傳遞信號變量,當操作完成之後會回調相關的方法,異步I/O也稱爲AIO。

5.3 多路複用器Selector

多路複用器的核心就是通過Selector來輪詢其上的Channel,當發現某個或者多個Channel處於就緒狀態後,從阻塞狀態返回就緒的Channel的選擇鍵集合,進行I/O操作。由於多路複用器是NIO實現非阻塞的關鍵,它又是主要通過Selector實現的。

5.4 僞異步 I/O

5.5 不同I/O模型對比

xxx

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