1. 程式人生 > 實用技巧 >(一)IO模型

(一)IO模型

一、IO模型介紹


為了更好地瞭解 IO模型,我們回顧下:同步,非同步,阻塞,非阻塞。

1,同步:

# 所謂同步,就是在發出一個功能呼叫時,在沒有得到結果之前,該呼叫就不會返回。
# 按照這個定義,其實絕大多數函式都是同步呼叫。但是一般而言,我們在說同步、非同步的時候,
# 特指那些需要其他部件協作或者需要一定時間完成的任務。

# 舉例:
# 1,multiprocessing.Pool 下的apply     # 發起同步呼叫後,就在原地等著任務結束,根本不考慮任務是在計算還是在io阻塞,總之就是一股腦地等任務結束
# 2,concurrent.futures.ProcessPoolExecutor().submit(func,).result()
# 3,concurrent.futures.ThreadPoolExecutor().submit(func,).result()

2,非同步:

# 非同步的概念和同步相對。當一個非同步功能呼叫發出後,呼叫者不能立刻得到結果。
# 當該非同步功能完成後,通過狀態、通知或回撥來通知呼叫者。
# 如果非同步功能用狀態來通知,那麼呼叫者就需要每隔一定時間檢查一次,效率就很低(有些初學多執行緒程式設計的人,總喜歡用一個迴圈去檢查某個變數的值,這其實是一 種很嚴重的錯誤)。
# 如果是使用通知的方式,效率則很高,因為非同步功能幾乎不需要做額外的操作。至於回撥函式,其實和通知沒太多區別。

# 舉例:
# 1,multiprocessing.Pool().apply_async() # 發起非同步呼叫後,並不會等待任務結束才返回,相反,會立即獲取一個臨時結果(並不是最終的結果,可能是封裝好的一個物件)。 # 2,concurrent.futures.ProcessPoolExecutor(3).submit(func,) # 3,concurrent.futures.ThreadPoolExecutor(3).submit(func,)

3,阻塞:

# 阻塞呼叫是指呼叫結果返回之前,當前執行緒會被掛起(如遇到io操作)。
# 函式只有在得到結果之後才會將阻塞的執行緒啟用。有人也許會把阻塞呼叫和同步呼叫等同起來,實際上他是不同的。
# 對於同步呼叫來說,很多時候當前執行緒還是啟用的,只是從邏輯上當前函式沒有返回而已。 # 舉例: # 1,同步呼叫:apply一個累計1億次的任務,該呼叫會一直等待,直到任務返回結果為止,但並未阻塞住(即便是被搶走cpu的執行許可權,那也是處於就緒態); # 2,阻塞呼叫:當socket工作在阻塞模式的時候,如果沒有資料的情況下呼叫recv函式,則當前執行緒就會被掛起,直到有資料為止。

4,非阻塞:

# 非阻塞和阻塞的概念相對應,指在不能立刻得到結果之前也會立刻返回,同時該函式不會阻塞當前執行緒。

5,總結:

# 1,同步與非同步針對的是函式/任務的呼叫方式:同步就是當一個程序發起一個函式(任務)呼叫的時候,一直等到函式(任務)完成,而程序繼續處於啟用狀態。
# 而非同步情況下是當一個程序發起一個函式(任務)呼叫的時候,不會等函式返回,而是繼續往下執行當,函式返回的時候通過狀態、通知、事件等方式通知程序任務完成。

# 2,阻塞與非阻塞針對的是程序或執行緒:阻塞是當請求不能滿足的時候就將程序掛起,而非阻塞則不會阻塞當前程序

同步(synchronous) IO和非同步(asynchronous) IO,阻塞(blocking) IO和非阻塞(non-blocking)IO分別是什麼,到底有什麼區別?

這個問題其實不同的人給出的答案都可能不同,比如 wiki,就認為asynchronous IO和non-blocking IO是一個東西。

這其實是因為不同的人的知識背景不同,並且在討論這個問題的時候上下文(context)也不相同。所以,為了更好的回答這個問題,我先限定一下本文的上下文。

"""
本文討論的背景是Linux環境下的network IO。本文最重要的參考文獻是Richard Stevens的“UNIX® Network Programming Volume 1, Third Edition: The Sockets Networking ”,6.2節“I/O Models ”,Stevens在這節中詳細說明了各種IO的特點和區別,如果英文夠好的話,推薦直接閱讀。Stevens的文風是有名的深入淺出,所以不用擔心看不懂。本文中的流程圖也是擷取自參考文獻。

Stevens在文章中一共比較了五種IO Model:  
* blocking IO  
* nonblocking IO  
* IO multiplexing  
* signal driven IO  
* asynchronous IO  
由signal driven IO(訊號驅動IO)在實際中並不常用,所以主要介紹其餘四種IO Model。
"""

再說一下 IO發生時涉及的物件和步驟。對於一個network IO \(這裡我們以read舉例\),它會涉及到兩個系統物件,一個是呼叫這個 IO的process \(or thread\),另一個就是系統核心\(kernel\)。當一個read操作發生時,該操作會經歷兩個階段:

# 1,等待資料準備 (Waiting for the data to be ready)

# 2,將資料從核心拷貝到程序中(Copying the data from the kernel to the process)

記住這兩點很重要,因為這些IO模型的區別就是在兩個階段上各有不同的情況。

補充:

# 1,輸入操作:read、readv、recv、recvfrom、recvmsg共5個函式,如果會阻塞狀態,則會經理wait data和copy data兩個階段,如果設定為非阻塞則在wait 不到data時丟擲異常

# 2,輸出操作:write、writev、send、sendto、sendmsg共5個函式,在傳送緩衝區滿了會阻塞在原地,如果設定為非阻塞,則會丟擲異常

# 3,接收外來連結:accept,與輸入操作類似

# 4,發起外出連結:connect,與輸出操作類似