淺談5中io模型和同步io、異步io及R模式P模式

   五種io模型:

  阻塞io模型 

 非阻塞io模型  

io複用模型 

 信號驅動io模型  

異步io模型


 其中阻塞與非阻塞

  阻塞io:就是函數調用後,會被掛起,直到內核接受到了io的讀寫,拷貝到了應用進程的用戶態後,函數才返回

 非阻塞io:就是函數調用後,立刻返回結果,這個結果告訴調用函數者內核是否接受並完成了io的讀寫


阻塞io 非阻塞io io複用  信號驅動io。這四個都可以認爲是同步io模型

有人會覺得,同步不是要等待需要的數據嗎,那麼同步就是阻塞了,異步就是非阻塞了。

其實不然,同步異步 和 阻塞非阻塞 兩種概念沒有絕對的一等一關係。

這麼說吧,同步io意思就是爲了需要的數據,在邏輯上會一直‘等待’,但這種‘等待’不一定非得是阻塞的等待,不停的while輪詢也算是等待。而最後等待完之後,由應用程序去拿數據,而並非完全由內核包辦。

這麼說就很清楚了吧,阻塞io的確是同步io,毋庸置疑的,例如TCP中的recv,爲了等待[連接fd]上的數據,永遠阻塞着,除非對端斷開(但是我之前也看過一個文章很有意思,認爲也存在異步阻塞io,事實是的確是這樣的,即異步之後,阻塞處理事件,這就有點牽強了,不過也能說得通,一般初學者接觸到的阻塞函數都是爲了同步io設計的,這樣記下來就好)。

 但是非阻塞io,比如把TCP的recv設置成非阻塞的,它會立刻返回一個值,一般來說肯定會是-1,(有人會說-1不是recv失敗嗎,注意recv有個特殊的條件,那麼就是返回值爲-1時,如果(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)成立,那麼仍然表示recv成功,而如果非阻塞的話,errno就會被設置爲EAGAIN。如果返回值-1,那麼一半還是不滿足我們需要接受數據的這個要求,我們會把它放到while裏不停的recv,也就是所謂的輪詢。那麼可以說這種模式仍然爲同步,雖然它的函數調用是非阻塞的,但它的行爲表現了它是爲了等待某種操作完成。只不過沒有阻塞那麼耿直的直接停在那了。

舉個例子,現在是8點,9點的時候有球賽。現在有兩種方式度過8-9點這一個小時。

阻塞的做法就是,坐在電視前的沙發上,乾巴巴地等着,減輕消耗,球賽一旦開始了,屏幕已經開始播了,我就知道了,直接開始看,這就是阻塞。

而非阻塞的做法就是,我這個人比較好動,這一個小時我不安分,一會兒去拿個零食,一會兒去玩個遊戲,但是每過一會兒還得回來盯一下屏幕看比賽開始沒,直到比賽開始了,屏幕顯示後一剎那,我又盯着屏幕,發現比賽開始了,然後我坐下看比賽。

這兩種行爲一個是阻塞,一個是非阻塞,但從本質上來看,我都是爲了等待球賽的開始。所以說,這就是同步的。

而同步io的特點就是io的操作由應用程序完成的,也就是說,如果我要recv一個buffer,好的我應用程序開始阻塞等待,或者輪詢等待,你內核如果收到了recv的數據,你就告訴我,我就不等待了,或者不輪詢了(這個取決於代碼設計了),我去recv我的buffer去了。不管是輪詢還是阻塞,都是應用程序自己主動要求的,不達目的決不罷休的感覺。

而異步io的特點就是io的操作由內核全部完成的,也就是說,比如aio_read,我要讀取一些數據存入buffer,好的我調用了函數,然後不管了,直接去執行別的操作了,你內核如果完成了,用個信號通知我就行了。這個就區別與同步的主動要求了,而換成了被動的接受,如果你內核io操作ok了,你給我個信號,我去做處理,你不給我信號,我也不會主動搭理你。

仍然拿球賽作爲例子,我看球賽,但是這時我換了一個超智能電視。8點開始,我給電視說,球賽開始了告訴我啊,我去幹別的事了,比如玩電腦,假如我真的玩過了,超過9點了,就代表我這個人(進程)沒有被等待球賽(等待io可讀數據)這件事束縛住,雖然錯過球賽不符合我的意願,但也沒辦法,我不會看時間,如果超級電視沒通知我,我就沒法採取措施(調用信號函數),但是這卻不會束縛我不能幹別的事,比如玩電腦;但是如果一切正常,然後到了9點,電視就響了告訴我球賽開始了,等於是相比起同步io的例子,我要自己觀測球賽的開始時間,然後去看。而異步io就是球賽開始了,電視(內核)通過響聲(信號)告訴我(應用程序)它幫我讀到球賽開始了(數據讀好了)

而io複用也是同步io,程序會阻塞在select,poll,epollwait這些io複用函數上,只不過它們可以同時阻塞多個fd上的io,所以叫‘復’用。說到底也算是主觀等待某些數據的到來,因此也算同步io。


因此可以總結下同步io和異步io的區分。

如果進行io操作的過程中,進程發生過等待(阻塞或者輪詢),那麼它就是同步的io。可以理解成進程必須和內核步調一致。

反之,如果全程沒有發生過等待,我有過io請求,你內核io操作完,給我一個提示,例如信號,我應用程序立刻去解決它,可以說,進程全程沒有阻塞過,進程運行的整個的時間段 中 內核後臺同時也io處理數據,可以說是異步io了。聽起來好像比起同步io挺高效的,但根據不同的場合和需求選擇同步io還是異步io吧,沒有哪個絕對的好壞,只有相對的適用。

  這裏又涉及到兩個模式,即r模式和p模式。

Reactor模式就是同步io,主線程epollwait客戶端上的可讀事件,如果有事件發生了,ok,交給工作線程去recv數據吧。

Proactor模式就是異步io,主線程同樣epollwait,但是這時並不像R模式那樣,讓線程去recv等待數據,而是用aio_read註冊‘已讀完’事件。然後'已讀完'事件如果發生。那麼之前ai_read會發回一個信號,信號處理函數裏會開啓線程處理數據,而數據的大小和地址都在aio_read註冊時被指定好了。線程拿到數據後,同時也會用aio_write註冊‘已寫完’事件,同樣也會有相應的信號處理函數,處理寫完數據後的事情。

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