五種傳統IO模型

public_tsing發表於2023-02-03

五種傳統I/O模型

作者:tsing

本文地址:https://www.cnblogs.com/TssiNG-Z/p/17089759.html

簡介

提起I/O模型, 就會說到同步/非同步/阻塞/非阻塞亂七八糟一大堆, 這裡簡單整理一下, 做個備忘.

正文

傳統I/O模型一共有5種 : 阻塞I/O, 非阻塞I/O, 多路I/O複用, 訊號驅動I/O, 非同步I/O.

為了更好的理解同步和非同步的區別, 這裡我們引入兩個概念 : 使用者程式 和 核心, 透過這兩個概念, 我們嘗試對I/O進行一個膚淺的解釋和理解: 當我們在程式中透過系統API進行系統呼叫,將核心中的資料讀入準備好的buffer時, 由於系統呼叫, 程式會進入阻塞, 當所資料被成功讀入了buffer後,系統呼叫結束, 上述這個讀取的過程, 我們可以理解為一次Input(Output則反過來).

而同步和非同步的區別的關鍵點就在於, 有沒有系統呼叫導致程式進入了阻塞.

下面我們回到傳統I/O模型, 解釋一下每種模型的行為(為了便於理解, 下面用read代表從核心讀取資料的系統呼叫):

  1. 阻塞I/O : 在需要資料時, 直接呼叫read, 要求從核心中讀取一定量的資料, 此時程式一直阻塞在這裡(等待核心資料準備就緒), 直到操作完成後, read返回讀入的位元組數(或者被訊號打斷).

  2. 非阻塞I/O : 在需要資料時, 呼叫read, 若此時核心中的資料沒有準備好, read會直接返回一個類似false的值, 表明資料沒準備好, 傳統的程式碼結構中, 我們會不斷呼叫read, 直至read返回類似true的值, 表明資料讀取成功. 在這種呼叫場景下, CPU通常會處於高佔用狀態.

  3. 多路I/O複用 : 即透過select/poll/epoll(epoll是在linux核心2.6版本之後加入的, win32和bsd中並沒有epoll這個介面, win32有效能更好IOCP, bsd中相似的介面則是kqueue)來檢查一個或多個裝置檔案描述符是否處於可讀(ready)狀態, 這三種呼叫中select和poll採用輪詢機制, epoll使用核心回撥, 所以epoll(O(1))的效能會比select/poll(O(n))要好, 當上述介面成功返回後, 我們就可以直接對返回的裝置檔案描述符進行read, 在這種場景下, 程式則阻塞在上述三個系統呼叫中的某一箇中, 而不是read這個系統呼叫.

  4. 訊號驅動I/O : 顧名思義, 透過向核心註冊一個特定訊號的回撥函式, 當I/O準備就緒時, 核心會向程式傳送特定訊號, 該訊號會被註冊好的回撥函式處理, 回撥函式中則包含了read系統呼叫等操作來將資料讀入buffer.

  5. 非同步I/O : 可以簡單理解為對訊號驅動I/O的一種升級, 在進行I/O操作之前, 先準備好buffer和回撥介面, 將buffer直接透過aio_read(asynchronous I/O)介面提交給系統核心, 核心會幫忙完成資料的複製工作, 當資料準備完成時, 回撥介面會被呼叫, 在該回撥中可以直接對buffer進行操作來處理資料(核心幫忙read過了), 而在此期間, 程式沒有因為系統呼叫進入過阻塞狀態. win32的IOCP就是一種非同步模型, boost的asio庫也是如此.

總結

我們可以將I/O模型分為兩類 : 同步I/O和非同步I/O, 其中同步I/O的關鍵在於系統呼叫導致程式進入阻塞, 故而上述的 阻塞I/O, 非阻塞I/O, 多路複用I/O 和 訊號驅動I/O都屬於同步I/O, 非同步I/O則可以參考win32 IOCP和boost asio.

參考文獻:

  • <<UNIX網路程式設計 卷一>>

以上, 如有錯誤疏漏或疑問, 歡迎指正討論, 轉載請註明.