Linux裡五種I/O模型

LudwigWuuu發表於2019-01-19

阻塞I/O

Linux裡五種I/O模型

如上圖,我們寫的應用程式使用資料流(UDP)Socket接收資料,呼叫recvfrom()系統函式接收網路卡上的資料。在阻塞I/O下,呼叫recvfrom()將造成程式阻塞,直到資料接收完畢。

記憶體空間有系統空間和使用者空間,並且都有相應的緩衝區:

  • 第一階段(wait for data等待資料準備):程式阻塞,核心接管,複製資料到系統空間裡的資料緩衝區(當然是能夠從網路卡上接收到資料)。接收完畢後,則資料準備完畢(datagram ready)。
  • 第二階段(copy data from kernel to user從核心拷貝資料到使用者緩衝區):這時候程式仍然阻塞,核心將系統空間內緩衝區ready的資料複製到使用者空間裡的使用者自己定義的緩衝區。拷貝完畢則喚醒程式。 整個階段,程式都是阻塞的,直到資料讀取到使用者緩衝區內。

非阻塞I/O

Linux裡五種I/O模型

如上圖,同樣應用程式呼叫資料流(UDP)Socket接收資料,呼叫recvfrom()系統函式,並且設定了Socket為non-blocking非阻塞。

這時我們呼叫系統函式recvfrom()則不阻塞程式,如果資料沒有在系統空間的緩衝區內準備好,則立即返回一個error,直接得到一個結果,這時候程式可以做點其他任務而不用阻塞在這裡。但是如果資料在系統空間內的緩衝區準備好了,則將開始從系統空間拷貝資料到使用者空間。

I/O 多路複用(IO multiplexing)

Linux裡五種I/O模型

I/O多路複用大致情況如上圖。I/O多路複用也就是我們常說的select、poll以及epoll,它能夠使一個程式能夠同時處理更多的連線。

這裡就以select為例:select()也是一個系統呼叫,如果一個程式呼叫了select()那麼這個程式也會被阻塞。

  • 第一階段:應用程式程式呼叫select(),這時該程式阻塞,不過核心會監視應用程式向select註冊的所有Socket。如果註冊的Socket中有資料以及在系統空間緩衝區裡準備好了的,則該程式被喚醒。
  • 第二階段:從select()的阻塞中返回,應用程式程式只能知道向select註冊的Socket裡有資料準備好的Socket,但是並不知道具體是哪個Socket資料準備好了。所以,應用程式需要自己遍歷Socket,找出資料準備好的Socket,然後呼叫recvfrom()函式將系統空間緩衝區內資料複製到使用者空間緩衝區內。

訊號驅動 I/O(signal driven IO)

Linux裡五種I/O模型

Linux核心定義了很多訊號,如SIGUSR1和SIGUSR2都是可以使用者自定義傳送/處理的訊號。

應用程式首先需要安裝訊號處理器(即提供一個回撥函式給核心),當相應事件發生的時候,作業系統會啟用該訊號,處理權交給程式,由程式呼叫處理該訊號的回撥函式。

在訊號驅動I/O中,應用程式先安裝訊號處理器,返回結果,程式可以繼續執行。當資料在系統空間內準備好了後,核心會啟用訊號,程式則會呼叫相應的回撥函式(這裡資料準備好了則直接呼叫recvfrom()函式),然後資料則直接拷貝到使用者空間緩衝區內。

非同步 I/O(asynchronous IO)

Linux裡五種I/O模型

非同步I/O則是呼叫aio_read()系統函式,傳入了使用者空間緩衝區的地址指標。呼叫後程式不會被阻塞,則可以做其他事情,核心會在將資料複製到系統空間緩衝區,然後再將資料複製到使用者提供的使用者緩衝區地址中。都做完了後,則核心傳送一個signal通知應用程式read完成了。這時應用程式之前提供的回撥函式,由該程式來執行。

相關文章