程序員成長之旅——同步IO和異步IO(五種IO模型)

在這裏首先要知道一點就是IO操作其實總的就分爲兩種,第一種是等待的時間,第二種就是數據的拷貝,而往往等待的時間都是高於數據拷貝的,因此爲了IO的更加高效,我們都是通過來縮短等待的時間來提生IO的效率的。其次在學習五種IO模型的前提下,先了解一下其概念。同步,異步,阻塞和非阻塞。

同步和異步

這兩個概念與消息的通知機制有關。

同步

所謂同步,就是在發出一個功能調用時,在沒有得到結果之前,該調用就不返回。比如,調用readfrom系統調用時,必須等待IO操作完成後才能返回。它是一個可靠的任務序列。

異步

異步的概念和其同步的概念是與之對應的,當一個異步過程調用發出後,調用者不能立即得到結果。實際處理這個調用的部件在完成後,通過狀態、通知、和回調來告訴調用者。實際處理的是否完成此任務,它是不得而知的,因此它是一個不可靠的任務序列。

消息通知

上面已知異步的概念和同步的概念是與之相對的。當一個同步的調用發出後,調用者要一直等待返回消息結果通知後,才能進行後續的執行;而當一個異步過程調用的時候,調用者不能立即得到返回消息。實際處理這個調用的部件在完成後,通過狀態、通知和回調來通知調用者的

這裏提到執行部件和調用者通過三種途徑返回結果:狀態、通知和回調。使用那一種通知機制,依賴於執行部件的實現,除非執行部件提供多種選擇,否則是不受調用者控制的

1.如果執行部件用狀態來通知,那麼調用者就需要每隔一定時間檢查一次,效率就很低;
2.如果使用通知的方式,效率則很高,因爲執行部件幾乎不需要做額外的操作。至於回調函數和通知幾乎一樣。

場景比喻

舉個例子,比如我去海底撈去吃飯,可能就有兩種方式:

1.選擇排隊等候;
2.另外選擇一個號碼,等到我的號碼的時候,廣播通知我去就餐;

第一種:前者(排隊等候)就相當於同步等待消息的通知,也就是我要一直等待前面客人用餐完。
第二種:後者(等待廣播通知)就是異步等待消息通知。在異步消息處理中,等待消息通知者(在這個例子中就是等待辦理業務的人)往往註冊一個回調機制,在所等待的事件被觸發時由觸發機制(在這裏是櫃檯的人)通過某種機制(在這裏是寫在小紙條上的號碼,喊號)找到等待該事件的人。

阻塞和非阻塞

阻塞和非阻塞與等待消息通知時的狀態有關。

阻塞

阻塞調用是指調用返回之前,當前線程會被掛起。函數只有在得到結果之後纔會返回。
阻塞和同步是兩種不同的概念。首先同步是對於消息的通知機制而言,阻塞是針對等待時的狀態來說的。而且對於同步調用來說,很多時候當前線程還是激活的,只是邏輯上當前沒有返回而已。

非阻塞

非阻塞和阻塞的概念相對應,指在不能立刻得到結果之前,該函數不會阻塞當前線程,而會立刻返回,並設置相應的errno。
雖然表面上看非阻塞的方式可以明顯的提高CPU的利用率,但是也帶了另外一種後果就是系統的線程切換增加。增加的CPU執行時間能不能補償系統的切換成本需要好好評估。

事例

以小王下載文件爲例,對上述概念做以理解:
1.同步阻塞:小王一直盯着下載進度條,到100%的時候就完成。
同步:等待下載完成通知;
阻塞:等待下載完成通知過程中,不能做其它任務處理;

2.同步非阻塞:小王提交下載任務後就去幹別的,每過一段時間就去瞄一眼進度條,看是否完成。
同步:等待下載完成通知;
非阻塞:等待下載完成通知過程中,去看動漫去了,只是時不時會瞄一眼進度條;【小王必須在兩個任務操作中來回切換,關注下載進度】
3.異步阻塞:小王打開了下載完成後的提示音,下載完成會通知他,不過在這期間,他一直會等待這個聲音。
異步:下載完成後的提示音;
阻塞:等待提示音的過程中,不能做其它任務處理;

4.異步非阻塞小王打開了下載完成後的提示音,下載完成會通知他,不過在這期間,他可以去幹其它事情。
異步:下載完成後的提示音;
非阻塞:等待提示音的過程中,去幹其它事情了,只需要接收提示音的通知;

同步IO

阻塞IO

同步阻塞IO模型是最常用、最簡單的模型。在Linux下,默認情況下,所有套接字都是阻塞的。下面以阻塞套接字的recvfrom的調用圖來說明阻塞:
在這裏插入圖片描述
從上面可看出,一個應用進程調用一個recvfrom請求,但是它不能立即收到回覆,知道數據返回,然後將數據從內核空間拷貝到用戶空間。
第一二階段皆是阻塞。
優缺點:優點是簡單,實時性高,響應及時無延遲,但缺點也很明顯,需要阻塞等待,性能差

非阻塞IO

與阻塞式I/O不同的是,非阻塞的recvform系統調用調用之後,進程並沒有被阻塞,內核馬上返回給進程,如果數據還沒準備好,此時會返回一個error(EAGAIN 或 EWOULDBLOCK)。進程在返回之後,可以處理其他的業務邏輯,過會兒再發起recvform系統調用。採用輪詢的方式檢查內核數據,直到數據準備好。再拷貝數據到進程,進行數據處理。
在linux下,可以通過設置socket套接字選項使其變爲非阻塞。下圖是非阻塞的套接字的recvfrom操作
在這裏插入圖片描述
如上圖,前三次調用recvfrom請求,但是並沒有數據返回,所以內核返回errno(EWOULDBLOCK),並不會阻塞進程。但是當第四次調用recvfrom,數據已經準備好了,然後將它從內核空間拷貝到程序空間,處理數據。
在非阻塞狀態下,IO執行的等待階段並不是完全的阻塞的,但是第二個階段依然處於一個阻塞狀態。
優缺點:
優點:能在等待任務完成的時間裏幹其他的事情(也就是後臺多個任務可以同時執行)。
缺點:任務完成的響應延遲增大了,因爲每過一段時間纔去輪詢一次read操作,而任務可能在兩次輪詢之間的任意時間完成。這會導致整體數據的吞吐量降低。

信號驅動IO

允許socket進行信號驅動IO,並註冊一個信號處理函數,進程繼續運行並不阻塞。當數據準備好的時候,進程會收到一個SIGIO信號,可以在信號處理函數中調用I/O操作函數處理數據。
在這裏插入圖片描述
阻塞在IO操作的第二個階段

多路轉接IO

IO 多路複用的好處就在於單個進程就可以同時處理多個網絡連接的IO。它的基本原理就是不再由應用程序自己監視連接,取而代之由內核替應用程序監視文件描述符。
在這裏插入圖片描述
優勢
  與傳統的多線程/多進程模型比,I/O多路複用的最大優勢是系統開銷小,系統不需要創建新的額外進程或者線程,也不需要維護這些進程和線程的運行,降底了系統的維護工作量,節省了系統資源。
主要應用場景:
  ①、服務器需要同時處理多個處於監聽狀態或者多個連接狀態的套接字;
  ②、服務器需要同時處理多種網絡協議的套接字,如同時處理TCP和UDP請求;
  ③、服務器需要監聽多個端口或處理多種服務;
  ④、服務器需要同時處理用戶輸入和網絡連接。

異步IO

上述四種IO模型都是同步的。相對於同步IO,異步IO不是順序執行。用戶進程進行aio_read系統調用之後,就可以去處理其他的邏輯了,無論內核數據是否準備好,都會直接返回給用戶進程,不會對進程造成阻塞。等到數據準備好了,內核直接複製數據到進程空間,然後從內核向進程發送通知,此時數據已經在用戶空間了,可以對數據進行處理了。
在這裏插入圖片描述

IO兩個階段,進程都是非阻塞的。

五種IO模型比對

在這裏插入圖片描述
其實前四種I/O模型都是同步I/O操作,他們的區別在於第一階段,而他們的第二階段是一樣的:在數據從內核複製到應用緩衝區期間(用戶空間),進程阻塞於recvfrom調用。相反,異步I/O模型在這等待數據和接收數據的這兩個階段裏面都是非阻塞的,可以處理其他的邏輯用戶進程將整個IO操作交由內核完成,內核完成後會發送通知。在此期間,用戶進程不需要去檢查IO操作的狀態,也不需要主動的去拷貝數據。

釣魚解析其五種IO模型

阻塞IO
張三去釣魚的時候,他只有一個魚鉤,當他將魚鉤放上魚餌放入水裏的那一刻起,他就一直在等待,只專注與這一件事情,直到有魚咬住魚餌,他拉起來,表示這個過程完成。
非阻塞IO
李四去釣魚的時候,同樣是只有一個魚鉤,當他放入水裏的那一刻之後,沒有魚兒上鉤的話,他會拉起來魚鉤,然後修整一下,可以幹一下其他事情,之後一直重複這個動作,直到魚兒上鉤,表示這個過程完成。
信號驅動IO
王五去釣魚的時候,同樣是一個魚鉤,但不同的是他釣魚的同時,將魚鉤上綁了一個鈴鐺,在此期間,他還可以和李四交流,一旦鈴鐺響了,他拉上來之後代表完成。
多路轉接IO
馬六他有多個魚竿,假設他可以忙的過來,只要有一個魚竿有響應,他就可以拉起來了,這個概率就相對比比較大了。
異步IO
田七去釣魚的時候,剛拿起裝備,這時候有急事呼叫他,但與此同時他還想吃釣到的魚,這時候,他就可以僱人幫他釣魚,等釣好之後打電話通知他,將其裝備魚給他,就代表這個過程完成了。
參考網址

以上有任何問題的,歡迎大佬們指正

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