1. 程式人生 > >linux五種IO模型

linux五種IO模型

  為了更好的理解五種IO模型,我們先來說一下幾個概念:同步,非同步,阻塞和非阻塞。

同步和非同步

  這兩個概念與訊息的通知機制有關。

同步

  所謂同步,就是在發出一個功能呼叫時,在沒有得到結果之前,該呼叫就不返回。比如,呼叫readfrom系統呼叫時,必須等待IO操作完成才返回。

非同步

  非同步的概念和同步相對。當一個非同步過程呼叫發出後,呼叫者不能立刻得到結果。實際處理這個呼叫的部件在完成後,通過狀態、通知和回撥來通知呼叫者。比如:呼叫aio_read系統呼叫時,不必等IO操作完成就直接返回,呼叫結果通過訊號來通知呼叫者。

阻塞與非阻塞

  阻塞與非阻塞與等待訊息通知時的狀態

有關。

阻塞 

  阻塞呼叫是指呼叫結果返回之前,當前執行緒會被掛起。函式只有在得到結果之後才會返回。  
  阻塞和同步是完全不同的概念。首先,同步是對於訊息的通知機制而言,阻塞是針對等待訊息通知時的狀態來說的。而且對於同步呼叫來說,很多時候當前執行緒還是啟用的,只是從邏輯上當前函式沒有返回而已。

非阻塞

  非阻塞和阻塞的概念相對應,指在不能立刻得到結果之前,該函式不會阻塞當前執行緒,而會立刻返回,並設定相應的errno。
  雖然表面上看非阻塞的方式可以明顯的提高CPU的利用率,但是也帶了另外一種後果就是系統的執行緒切換增加。增加的CPU執行時間能不能補償系統的切換成本需要好好評估。

事例

  以小明下載檔案為例,對上述概念做一梳理:
   
①、同步阻塞:小明一直盯著下載進度條,到 100% 的時候就完成。
 
  同步:等待下載完成通知;
  阻塞:等待下載完成通知過程中,不能做其他任務處理;

②、同步非阻塞:小明提交下載任務後就去幹別的,每過一段時間就去瞄一眼進度條,看到 100% 就完成。

  同步:等待下載完成通知;
  非阻塞:等待下載完成通知過程中,去幹別的任務了,只是時不時會瞄一眼進度條;【小明必須要在兩個任務間切換,關注下載進度】

③、非同步阻塞:小明換了個有下載完成通知功能的軟體,下載完成就“叮”一聲。不過小明仍然一直等待“叮”的聲音(看起來很傻,不是嗎)。

  非同步:下載完成“叮”一聲通知;
  阻塞:等待下載完成“叮”一聲通知過程中,不能做其他任務處理;

④、非同步非阻塞:仍然是那個會“叮”一聲的下載軟體,小明提交下載任務後就去幹別的,聽到“叮”的一聲就知道完成了。
  
  非同步:下載完成“叮”一聲通知;
  非阻塞:等待下載完成“叮”一聲通知過程中,去幹別的任務了,只需要接收“叮”聲通知。

五種IO模型

阻塞式I/O
非阻塞式I/O
I/O複用(select,poll,epoll等)
訊號驅動式I/O(SIGIO)
非同步I/O(POSIX的aio_系列函式)

IO執行的兩個階段

  在Linux中,對於一次讀取IO的操作,資料並不會直接拷貝到程式的程式緩衝區。通常包括兩個不同階段:
 (1)等待資料準備好,到達核心緩衝區;
 (2)從核心向程序複製資料。
  對於一個套接字上的輸入操作,第一步通常涉及等待資料從網路中到達。當所有等待分組到達時,它被複制到核心中的某個緩衝區。第二步就是把資料從核心緩衝區複製到應用程式緩衝區。

阻塞式I/O模型:

  同步阻塞 IO 模型是最常用、最簡單的模型。在linux中,預設情況下,所有套接字都是阻塞的。 下面我們以阻塞套接字的recvfrom的的呼叫圖來說明阻塞:
這裡寫圖片描述
  程序呼叫一個recvfrom請求,但是它不能立刻收到回覆,直到資料返回,然後將資料從核心空間複製到程式空間。
  在IO執行的兩個階段中,程序都處於blocked(阻塞)狀態,在等待資料返回的過程中不能做其他的工作,只能阻塞的等在那裡。
  
優缺點:
  優點是簡單,實時性高,響應及時無延時,但缺點也很明顯,需要阻塞等待,效能差;

非阻塞式I/O:

  與阻塞式I/O不同的是,非阻塞的recvform系統呼叫呼叫之後,程序並沒有被阻塞,核心馬上返回給程序,如果資料還沒準備好,此時會返回一個error(EAGAIN 或 EWOULDBLOCK)。程序在返回之後,可以處理其他的業務邏輯,過會兒再發起recvform系統呼叫。採用輪詢的方式檢查核心資料,直到資料準備好。再拷貝資料到程序,進行資料處理。
  在linux下,可以通過設定socket套接字選項使其變為非阻塞。下圖是非阻塞的套接字的recvfrom操作
這裡寫圖片描述
  如上圖,前三次呼叫recvfrom請求,但是並沒有資料返回,所以核心返回errno(EWOULDBLOCK),並不會阻塞程序。但是當第四次呼叫recvfrom,資料已經準備好了,然後將它從核心空間拷貝到程式空間,處理資料。
  在非阻塞狀態下,IO執行的等待階段並不是完全的阻塞的,但是第二個階段依然處於一個阻塞狀態。
  
同步非阻塞方式相比同步阻塞方式
  優點:能夠在等待任務完成的時間裡幹其他活了(包括提交其他任務,也就是 “後臺” 可以有多個任務在同時執行)。
  缺點:任務完成的響應延遲增大了,因為每過一段時間才去輪詢一次read操作,而任務可能在兩次輪詢之間的任意時間完成。這會導致整體資料吞吐量的降低。

I/O多路複用(select,poll,epol):

  IO 多路複用的好處就在於單個程序就可以同時處理多個網路連線的IO。它的基本原理就是不再由應用程式自己監視連線,取而代之由核心替應用程式監視檔案描述符。
  以select為例,當用戶程序呼叫了select,那麼整個程序會被阻塞,而同時,kernel會“監視”所有select負責的socket,當任何一個socket中的資料準備好了,select就會返回。這個時候使用者程序再呼叫read操作,將資料從核心拷貝到使用者程序。如圖:
這裡寫圖片描述
  這裡需要使用兩個system call (select 和 recvfrom),而阻塞 IO只調用了一個system call (recvfrom)。所以,如果處理的連線數不是很高的話,使用IO複用的伺服器並不一定比使用多執行緒+非阻塞阻塞 IO的效能更好,可能延遲還更大。IO複用的優勢並不是對於單個連線能處理得更快,而是單個程序就可以同時處理多個網路連線的IO。
  實際使用時,對於每一個socket,都可以設定為非阻塞。但是,如上圖所示,整個使用者的程序其實是一直被阻塞的。只不過程序是被select這個函式阻塞,而不是被IO操作給阻塞。所以IO多路複用是阻塞在select,epoll這樣的系統呼叫之上,而沒有阻塞在真正的I/O系統呼叫(如recvfrom)。

優勢
  與傳統的多執行緒/多程序模型比,I/O多路複用的最大優勢是系統開銷小,系統不需要建立新的額外程序或者執行緒,也不需要維護這些程序和執行緒的執行,降底了系統的維護工作量,節省了系統資源。
主要應用場景
  ①、伺服器需要同時處理多個處於監聽狀態或者多個連線狀態的套接字;
  ②、伺服器需要同時處理多種網路協議的套接字,如同時處理TCP和UDP請求;
  ③、伺服器需要監聽多個埠或處理多種服務;
  ④、伺服器需要同時處理使用者輸入和網路連線。

訊號驅動式I/O

  允許Socket進行訊號驅動IO,並註冊一個訊號處理函式,程序繼續執行並不阻塞。當資料準備好時,程序會收到一個SIGIO訊號,可以在訊號處理函式中呼叫I/O操作函式處理資料。如下圖:
這裡寫圖片描述
  阻塞在IO操作的第二階段

非同步I/O模型:

  上述四種IO模型都是同步的。相對於同步IO,非同步IO不是順序執行。使用者程序進行aio_read系統呼叫之後,就可以去處理其他的邏輯了,無論核心資料是否準備好,都會直接返回給使用者程序,不會對程序造成阻塞。等到資料準備好了,核心直接複製資料到程序空間,然後從核心向程序傳送通知,此時資料已經在使用者空間了,可以對資料進行處理了。
  在 Linux 中,通知的方式是 “訊號”,分為三種情況:
  ①、如果這個程序正在使用者態處理其他邏輯,那就強行打斷,呼叫事先註冊的訊號處理函式,這個函式可以決定何時以及如何處理這個非同步任務。由於訊號處理函式是突然闖進來的,因此跟中斷處理程式一樣,有很多事情是不能做的,因此保險起見,一般是把事件 “登記” 一下放進佇列,然後返回該程序原來在做的事。
  ②、如果這個程序正在核心態處理,例如以同步阻塞方式讀寫磁碟,那就把這個通知掛起來了,等到核心態的事情忙完了,快要回到使用者態的時候,再觸發訊號通知。
  ③、如果這個程序現在被掛起了,例如陷入睡眠,那就把這個程序喚醒,等待CPU排程,觸發訊號通知。
這裡寫圖片描述
  IO兩個階段,程序都是非阻塞的

五種IO模型比較

這裡寫圖片描述
  其實前四種I/O模型都是同步I/O操作,他們的區別在於第一階段,而他們的第二階段是一樣的:在資料從核心複製到應用緩衝區期間(使用者空間),程序阻塞於recvfrom呼叫。相反,非同步I/O模型在這等待資料和接收資料的這兩個階段裡面都是非阻塞的,可以處理其他的邏輯使用者程序將整個IO操作交由核心完成,核心完成後會發送通知。在此期間,使用者程序不需要去檢查IO操作的狀態,也不需要主動的去拷貝資料。