1. 程式人生 > 其它 >聊聊五種 I/O 模型

聊聊五種 I/O 模型

什麼是 I/O

I/O 是 Input/Output 的簡寫,即輸入/輸出,是計算機與外部裝置(鍵盤、滑鼠、磁碟等)通訊的統稱,與具體實現無關。

與外部裝置的通訊其實就是對外部裝置進行讀取或寫入資料的過程,比如對檔案的讀寫操作可以稱為檔案 I/O、對套接字的讀寫操作稱為網路 I/O。

轉載請註明來源地址:她和她的貓

同步 I/O 和非同步 I/O

同步和非同步是相對於獲取資料的過程而言的。

  • 同步 I/O:包含阻塞 I/O、非阻塞 I/O、I/O 多路複用、訊號驅動式I/O 四種 I/O 模型,它們的共同點就是在執行 I/O 操作時會阻塞程序,直到 I/O 操作完成。
  • 非同步 I/O:執行 I/O 操作時不會阻塞程序。

在使用者程序中執行 read 系統呼叫時,核心將資料從核心空間拷貝使用者程序空間。如果沒有讀到資料,那麼程序會一直處於阻塞狀態,當讀取到資料時才會恢復程序,繼續執行後面的邏輯,所以我們稱這個操作是同步的。

非同步只需要執行 aio_read 系統呼叫告訴核心從哪兒讀取資料就可以了,程序不用等待立即返回,核心會非同步地將資料從核心空間拷貝到使用者程序空間。

阻塞 I/O 模型

套接字在建立時預設就是阻塞的。

使用者程序執行 read 系統呼叫,如果資料沒有準備好,那麼程序將會被掛起,直到資料準備好之後核心將資料拷貝給使用者程序。

舉一個燒開水的例子,水壺是套接字,水壺裡的水是資料,水未燒開說明資料沒有準備好。

現在有 A、B、C 三個水壺,程序查詢 A 的狀態,發現水還沒有燒開就一直等著水燒開,哪怕 B、C 的水燒開了也不管,直到 A 的水燒開了才會去處理下一個水壺。

缺點:每次只能對一個套接字進行操作,就算其它套接字資料準備好了也沒辦法立即處理。

非阻塞 I/O 模型

在 PHP 中可以呼叫 socket_set_nonblock 函式將套接字設定為非阻塞的。

非阻塞 I/O 在資料未準備好的情況下,執行 read 系統呼叫將會立即返回,應用程式可以使用迴圈不停地輪訓核心,直到資料準備好,核心將資料從拷貝到應用程式中。

虛擬碼:

while (true) {
    rbytes = read(fd);
    if (rbytes < 0 && errno == EWOULDBLOCK) {
        continue;
    }
    // 處理資料
}

用燒開水的例子來解釋就是,程序不斷地輪詢每個水壺的狀態,當某個水壺的水燒開了之後就執行下一步操作。

缺點:需要不停地輪詢核心,浪費系統資源。

I/O 多路複用模型

多路複用就是多個網路請求複用同一個程序,讓單程序的應用程式擁有了同時處理多個套接字的能力,避免不停地輪詢核心,造成資源浪費。

將需要監聽的套接字交給核心,然後程序被掛起陷入休眠狀態,當套接字資料準備好時,核心將對應的套接字及事件返回給程序並喚醒程序,程序就可以執行 read 系統呼叫讀取資料,這個時候資料肯定是準備好的。

目前常見的有三種多路複用機制,分別是 select、poll、epoll。

多路複用機制 平臺支援 底層實現 時間複雜度 最大連線數 fd 拷貝
select Linux/Windows 陣列 O(n) 1024 每次呼叫 select 都需要從使用者程序拷貝到核心
poll Linux 連結串列 O(n) 無上限 每次呼叫 poll 都需要從使用者程序拷貝到核心
epoll Linux 紅黑樹 O(1) 無上限 呼叫 epoll_ctl 時需要從使用者程序拷貝到核心,epoll_wait 不需要

從上表可以看出 epoll 效能最好,所以當程式執行在 Linux 系統上應該使用 epoll,如果是 Windows 可以使用 select,這樣我們就可以實現跨平臺的多路複用應用程式。

燒開水的例子:將 A、B、C 三個水壺交給核心,當有水燒開時核心就通知程序哪個水壺燒開了。

I/O 多路複用中的套接字必須設定為非阻塞的。

訊號驅動式 I/O 模型

呼叫 sigaction 安裝 SIGIO 訊號處理器,為套接字設定宿主程序,當套接字的資料準備好時,作業系統會觸發 SIGIO I/O 就緒訊號,就會執行安裝的訊號處理器,在訊號處理器中執行 I/O 操作。

程序會安裝 SIGIO 訊號的處理函式,讓核心有資料準備好時就觸發 SIGIO 訊號,並執行該訊號對應的處理函式。

用水壺的例子來解釋就是給每個水壺都安裝了一個蜂鳴器,當水燒開時就開始響,程序就知道哪個水壺燒開了。

非同步 I/O 模型

前面這四種 I/O 模型都是同步 I/O,不管是程序是如何知道資料是否準備好的,最終執行 read 系統呼叫從核心拷貝資料到使用者程序的過程是同步的,而非同步 I/O 的區別就在於這裡。

非同步 I/O 的讀寫操作都是立即返回的,讀寫操作由核心非同步地執行,資料拷貝的過程不會阻塞使用者程序。

相當於告訴核心,水燒開了就倒在這個杯子裡,我想喝水了就自己去喝。

總結

同步 I/O 和非同步 I/O 的區別:核心將資料從核心空間拷貝到使用者程序空間時,是否會阻塞使用者程序。
阻塞 I/O 和非阻塞 I/O 的區別:資料沒有準備好的時候是否會阻塞使用者程序。

作者:她和她的貓_her-cat,轉載請註明原文連結:https://www.cnblogs.com/her-cat/p/15093458.html,部落格地址:她和她的貓