IO模式 select、poll、epoll

阻塞(blocking)、非阻塞(non-blocking):最常聽到阻塞與非阻塞這兩個詞就是在函數調用中,比如waitid這個函數,通過NOHANG參數可以把waitid設置爲非阻塞的,也就是問詢一遍子進程,看看有沒有可回收子進程,如果沒有,直接返回不等待,而如果不設置NOHANG,waitid會一直問詢子進程,直到有一個可回收子進程再返回,也就是說如果沒有子進程可回收,waitid會一直阻塞在這條函數調用上,後面的操作就無法執行。如果把函數調用籠統的規劃到一個事件的執行,那麼阻塞與非阻塞的區別就是 是否僅僅只等待事件完成(事件返回)才進行下一步操作。比如 燒開水這個例子(參考資料中有提到),如果人從水壺開始燒水到水燒開這段時間一直在水壺前等待燒水,這就是阻塞,如果人比較聰明,在水壺燒水的過程中,人去看電視了或者幹其他事情了,這就是不阻塞的,但是在這種情況下,人想要知道水是否燒開了,只能隔一段時間去看一下水壺“燒開了沒有?”,這個隔一段時間去看一下的方法 就是 輪詢。所以,再回到waitid這個例子,我們設置NOHANG參數的時候,一般都要在waitid上加一個用於輪詢的循環。

同步(synchronous)、異步(asynchronous):同步和異步,一開始看很容易把同步和阻塞的概念混淆在一起,我傾向於將同步理解爲一種現象,而阻塞/非阻塞作爲這個現象中的某種狀態存在。同步和異步的區別,參考資料中的說法我覺得雖然把同步異步區別說出來了,但是同步和阻塞還是很容易混淆。那我再對燒開水這個例子重寫一下以供參考,水壺分爲 普通水壺 和 智能水壺,普通水壺需要人手工操作,需要人判斷水是否燒開,水燒開了需要人把水壺從火上拿下來(不然會溢出來),而智能水壺提供兩個作用,水燒開後自動關火併且發出響聲提示人水已經燒好。燒開水就分爲了兩個過程,等待水開和拿開水壺,首先說同步和異步,同步就是使用普通水壺燒水,水燒開後需要人主動拿開水壺,而等待水燒開的這個過程可以是阻塞(站那等)的和非阻塞(看電視輪詢)的,整個操作需要人的全程參與;異步就是使用智能水壺燒水,等待水開的過程人可以幹任何事(即異步本身就是非阻塞的,所以異步並不區分阻塞/非阻塞)並且人不用關心水是否燒開(敲重點),水燒開後,智能水壺會發出響聲提示人(發出信號),這完成了等待水開這第一過程,緊接着,智能水壺會自動關火,這代替了人拿開水壺的這個動作完成了第二過程。可以發現,同步整個過程需要人全程參與,而異步整個過程是兩個“人”在做,一個“人”就是這個智能水壺,一個人就是始終在做其他事的人,這個人將燒開水這件事託管給了智能水壺由智能水壺完成一切過程,最後提示人完成了。因此,判斷同步只需要看,事件的監管和完成方是不是事件發起者即可。

在接下來即將要說到的IO操作中,異步IO就是進程將IO操作完全交由內核控制,由內核中的DMA控制器完成全部IO操作並在完成後通過信號提示進程IO操作已完成,這也是兩個“人”的事情,進程將IO託管給DMA控制器,而進程又可以幹其他的事,二者在不同的事件上各自執行操作,是並行的。

IO模式:

比如對於一個read操作發生時,它會經歷下面兩個階段:

A, 等待數據準備,數據是否拷貝到內核緩衝區;

B, 將數據從內核拷貝到用戶進程空間

如果把這兩個階段對應到前文中我寫的燒開水的兩個過程中,對於下面的幾種IO模式就容易理解的多。

《Unix網絡編程卷1:套接字聯網API》(即UNP)中第六章對unix 系統將IO模型分爲五類:阻塞IO,非阻塞IO,IO複用,信號驅動,異步IO

1、阻塞IO:A階段阻塞等待,進程調用recvfrom詢問內核數據是否準備好,直到數據準備好再返回。(注意:由內核監聽數據,進程詢問內核數據是否準備好)

2、非阻塞IO:A階段非阻塞等待,即採用輪詢機制(即進程不斷的詢問內核數據是否準備好(不斷調用recvfrom),這麼做往往消耗大量CPU時間)判斷數據是否拷貝到內核緩衝區。

3、IO複用:IO複用技術是對阻塞IO的改進,首先非阻塞大量消耗CPU,不予考慮。在數據準備階段,內核監聽一個IO也好,監聽一羣IO也好,都在監聽,因此複用,就是內核同時監聽進程的多個IO操作,在進程詢問時返回有數據到來的IO。此時進程不是阻塞在recvfrom上,而是阻塞在IO複用系統調用——select、poll、epoll上,這幾個系統調用會在所監聽的所有IO中某個或某幾個有數據到來時返回。

多路IO共用一個同步阻塞接口,任意IO可操作都可激活IO操作,這是對阻塞IO的改進(主要是select和poll、epoll,關鍵是能實現同時對多個IO端口進行監聽)。此時阻塞發生在select/poll的系統調用上,而不是阻塞在實際的I/O系統調用上。IO多路複用的高級之處在於:它能同時等待多個文件描述符,而這些文件描述符(套接字描述符)其中的任意一個進入讀就緒狀態,select等函數就可以返回。

4、信號驅動

註冊一個IO信號事件,在數據可操作時通過SIGIO信號通知線程,這應該算是一種異步機制;

5、異步IO:進程通知內核完成一個IO操作,由內核完成A+B階段後通知進程。

參考中的這張圖很好的解釋了上述5種IO模型。

 

 

 

POSIX將IO只分成了同步IO、異步IO兩種模型。

同步I/O操作:實際的I/O操作將導致請求進程阻塞,直到I/O操作完成。

異步I/O操作:實際的I/O操作不導致請求進程阻塞。

由此,阻塞式I/O,非阻塞式I/O,I/O複用,信號驅動I/O模型都屬於同步I/O,因爲第二階段的數據複製都是阻塞的,也可以理解爲都由進程完成。而只有異步I/O模型屬於這裏的異步I/O操作(IO由內核完成)。

參考資料:知乎Linux分享官

bilibili視頻

《Unix網絡編程卷1:套接字聯網API(第3版)》6.2 IO模型

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章