在Unix系統中,一切都是檔案,都是二進位制流。在資訊交換的過程中,我們都是對這些流進行操作,即我們通常所說的
I/O(input output)操作
,計算機透過對每個流記錄一個檔案描述符——fd(file descriptor)來區分對每個檔案的操作!
下圖為最基本的 I/O 操作
下圖為系統呼叫
3.1 Blocking I/O Model
阻塞的I/O模型是最傳統,最基本的模型,即當程式呼叫讀取操作時,會一直阻塞到核心資料返回!
上圖展示了當程式執行recvfrom
函式時,透過系統呼叫,由使用者態轉入核心態,由於資料遲遲未到,所以核心一直會等待資料,直到資料準備好便將全部資料複製給使用者程式!此情況使用者程式是全程陷入阻塞狀態
阻塞I/O——當你下樓拿快遞,發現快遞小哥沒有來,ok沒辦法,我原地等,直到快遞小哥來了,拿到了快遞我再回宿舍!
3.2 Nonblocking I/O Model
非阻塞 I/O 即當使用者程式呼叫讀取操作時,如果核心資料準備好了則直接複製並返回;如果沒有準備好,那就返回一個錯誤提示程式,並不斷的去輪詢是否準備好!
上圖展示了當程式執行recvfrom
函式時,透過系統呼叫,由使用者態轉入核心態,由於資料遲遲未到,所以核心不會等待資料,直接返回一個錯誤給使用者程式!然後使用者程式不斷去輪詢,檢查資料是否準備好此情況使用者程式不斷去輪詢核心資料是否準備好,浪費CPU時間片
非阻塞I/O-當你下樓拿快遞,發現快遞小哥沒來,好吧我先回宿舍。剛回宿舍我心想:萬一此時恰好來了呢?於是我又馬上下樓去檢查,重複這個過程直到快遞小哥來!
3.3 I/O Multiplexing Model
I/O 多路複用即透過執行 select 或者 poll 函式來對多個檔案描述符進行監控,一旦核心發現某個資料準備好,便通知相對應的使用者程式進行讀取
上圖展示了程式一直阻塞在對 select 或者 poll 函式的呼叫上,等待直到某個 socket 可讀,此時程式便對資料進行讀取。這種模型有個明顯的缺點:需要兩次系統呼叫。而且當一個連線來了,就必須遍歷所有已經註冊的檔案描述符,來找到那個需要處理資訊的檔案描述符,如果已經註冊了幾萬個檔案描述符,那會因為遍歷這些已經註冊的檔案描述符,導致cpu爆炸。但其優勢在於可以對多個socket進行監控
I/O多路複用的優勢並不是對於單個連線能處理的更快,而是在於可以在單個執行緒/程式中處理更多的連線。與多程式和多執行緒技術相比,I/O多路複用技術的最大優勢是系統開銷小,系統不必建立程式/執行緒,也不必維護這些程式/執行緒,從而大大減小了系統的開銷。
select,poll 和 epoll 比較
1. 支援一個程式所能開啟的最大連線數
select:單個程式所能開啟的最大連線數有FD_SETSIZE宏定義,其大小是32個整數的大小(在32位的機器上,大小就是3232,同理64位機器上FD_SETSIZE為3264),當然我們可以對進行修改,然後重新編譯核心,但是效能可能會受到影響,這需要進一步的測試。
poll:poll本質上和select沒有區別,但是它沒有最大連線數的限制,原因是它是基於連結串列來儲存的。
epoll:雖然連線數有上限,但是很大,1G記憶體的機器上可以開啟10萬左右的連線,2G記憶體的機器可以開啟20萬左右的連線。
2. fd 劇增後帶來的IO效率問題
select:因為每次呼叫時都會對連線進行線性遍歷,所以隨著FD的增加會造成遍歷速度慢的“線性下降效能問題”。
poll:同上
epoll:因為epoll核心中實現是根據每個fd上的callback函式來實現的,只有活躍的socket才會主動呼叫callback,所以在活躍socket較少的情況下,使用epoll沒有前面兩者的線性下降的效能問題,但是所有socket都很活躍的情況下,可能會有效能問題。
3. 訊息傳遞方式
select:核心需要將訊息傳遞到使用者空間,都需要核心複製動作
poll:同上
epoll:epoll透過核心和使用者空間共享一塊記憶體來實現的。
I/O 多路複用——同樣去拿快遞,但是有中通,申通,韻達等多個快遞都快來了,於是我一直等電話,當某個快遞小哥給我打電話的時候我就去拿。
3.4 Signal-Driven I/O Model
訊號驅動型I/O即程式在IO訪問時,透過使用訊號告訴核心,當socket上的資料準備好了的時候,透過訊號
通知一聲
上圖展示了使用者程式先透過sigaction系統呼叫,建立一個訊號處理函式,立即返回。之後程式並不阻塞,而是轉而執行其他事情。當核心準備好資料後,產生一個SIGIO訊號(電平觸發)並投遞給訊號處理函式。可以在此函式中呼叫recvfrom函式運算元據從核心空間複製到使用者空間,這段過程程式阻塞。無論我們如何處理訊號,這個模型的優點是在等待資料包到達時不會阻塞。主迴圈可以繼續執行並等待訊號處理程式通知資料已準備好處理或資料包已準備好讀取。
訊號驅動型I/O——同樣去拿快遞,而且有中通,申通,韻達等多個快遞都快來了,我不等電話了(這個電話與上面電話不同,此處的電話需要專用的快遞電話),我先收拾宿舍衛生,一旦電話響了即某個快遞來了,那我就去拿!
3.5 Asynchronous I/O Model
非同步 I/O 模型即在程式發起非同步IO請求,立即返回。當核心完成IO時,核心給程式發一個訊號。
上圖展示了使用者程式 會首先呼叫 aio_read
函式執行系統呼叫,並向核心傳遞描述符、緩衝區指標、緩衝區大小(與read相同的三個引數)、檔案偏移量(與lseek類似),以及如何在整個操作完成時通知我們。這個系統呼叫立即返回,我們的程式並沒有阻塞而是繼續執行其他事情,直到資料全部寫入緩衝區!!!
非同步 I/O ——同樣拿快遞,我太忙了,委託給我同學去拿,並把快遞的單號,型別告訴他,然後我去吃飯或者幹其他事 ,當他拿到快遞時通知我一下,舒服
綜上,前四個I/O模型都是同步型 I/O,即都會在資料從核心寫入到緩衝區這個階段阻塞,即同步I/O操作會導致請求程式被阻塞,直到I/O操作完成。非同步I / O操作不會引起請求程式被阻塞
本作品採用《CC 協議》,轉載必須註明作者和本文連結