Linux之——五種IO模型

一. 同步與異步

    之前在對線程的談論中提到了線程對臨界資源訪問的一個同步與互斥的關係,這裏要強調,在IO模型中的同步與異步與線程的同步與互斥完全不是一回事。

    

    所謂同步,就是指當調用者發出調用的時候,在沒有得到結果之前調用並不返回,而是調用者自身一直在那裏等待結果,至於等待的方式不同可以分爲不同IO模型,下面會進行具體討論;因此,這裏的同步就可以理解爲:調用者發出的調用(如一個系統調用函數)和所需要的結果是保持一致性也就是同步性的,你不給我結果,我就一直在那裏等着就不返回,我不能單飛我一定要帶着結果一起走,對於結果的等待和提取都由調用者來完成,這就是同步

    至於異步,就是調用者發出調用後就直接返回了,當然就不會有返回結果,至於等待結果和獲取結果的事都由處理調用的部件來完成,完成之後再通過狀態、通知或者回調來告訴調用者,這時調用者纔會再回來拿到結果去處理;因此,這裏的異步同樣可以理解爲:調用者發出的調用可以不帶着結果一起自己先返回,而這時候調用者就是你沒有結果我自己先走,等結果都準備妥當當的了你通知我就行我再回來拿走處理,對於結果的等待和搬遷複用都不經過調用者之手而是由別人來完成,這就是異步



二. 阻塞與非阻塞

    阻塞嘛,顧名思義就是被阻擋在那除了等就不能幹別的了,當調用者發起調用時在結果返回之前調用者就被阻塞住不能再繼續往下執行了,也就是當前線程或進程會被掛起等待;這裏和同步是沒有關係的,同步是無關進程或線程是否被阻塞的,只是強調結果的等待和獲取都是自身來完成,而阻塞就是阻塞,是指當前進程或線程的運行狀態;

    非阻塞就是和阻塞相反,指當前進程或線程一直在運行中,在沒有結果返回的時候並不會掛起該進程或者線程使其只等待什麼都不做;同樣的,這裏和同步或者異步也並沒有什麼關係;



三. 五種IO模型

    對於系統的IO也就是數據的接收和發送來說,是要必經兩個過程的:一個是待數據的到來或者準備;二是當數據準備好或者到來之後要將其從內核提取到用戶內存中以便處理,也就是數據的搬遷;因此,任何IO模型都要經過這兩個重要的過程來進行數據的傳輸和處理;


  1. 阻塞IO模型

    在阻塞IO模型中,從調用系統函數獲取數據開始到得到數據,當前的進程或者線程始終是處於阻塞狀態的,也就是什麼都不幹,直到等完數據準備好和將數據搬遷到用戶空間爲止:

wKioL1dCazqT0gUAAAAPHT3YnIs274.png

  1. 用戶首先發出系統調用函數希望獲取數據;

  2. 系統調用會進入內核檢查是否有數據準備完畢,如果沒有就一直等待;

  3. 當數據準備完畢的時候會將數據拷貝到用戶空間;

  4. 拷貝完畢返回一個獲取數據成功的返回值來告訴用戶可以進行數據的處理了;在此期間,用戶進程或者線程一直是處於阻塞狀態的,無論是等待數據還是進行數據的拷貝;


2. 非阻塞IO模型

    和阻塞式的IO模型不同,當發出了系統調用的時候,如果這時候數據還沒有準備好,進程或線程並不會進入阻塞模式一直等待,而是會反覆輪詢“數據好沒...數據好沒...數據好沒...”,這是比較耗CPU資源的,而當數據準備好之後會和阻塞IO模型一樣進行數據的拷貝:

wKiom1dCbHvQ-30lAAAQOlapjIw722.png

  1. 首先用戶發出系統調用,去向內核申請獲取想要的數據;

  2. 內核檢查發現數據還沒準備好,就會返回一個錯誤值;

  3. 用戶接收到錯誤值並不甘心,就會反覆反覆詢問內核是否有數據準備好;

  4. 內核一直檢查直到有數據準備完畢,進行數據的搬遷,這時用戶會進入阻塞等待狀態等待數據拷貝完畢;

  5. 數據提取到用戶空間之後會返回一個成功狀態通知用戶數據完畢,這時用戶就可以進行數據的處理了;


3. IO複用模型

    既然是複用,說明一次性可以管理或處理多個IO,主要是靠select函數或者poll(epoll)函數來完成;這些函數同樣會阻塞進程,但是和阻塞式IO不同的是IO複用模型可以一次性阻塞多個IO操作,期間只要有一個IO接口的數據準備完畢就會返回通知用戶並且進行系統調用獲取數據:

wKiom1dCcRWQQalGAAAT2gIfcbg954.png

  1. 用戶首先調用select或者poll函數對多個IO接口操作進行檢測,同時會使進程或者線程阻塞;

  2. 當有至少一個IO接口響應的時候,系統就會通知內核調用相應的函數來獲取數據;

  3. 這時內核將數據拷貝遷移至內核空間,進程或者線程仍然處於阻塞狀態;

  4. 數據就緒,返回一個成功值告訴用戶可以處理數據了;


4. 信號驅動IO模型

    用戶首先註冊一個處理IO信號的信號處理函數,當數據還沒準備好的時候進程或者線程並不阻塞,當數據準備好的時候用戶進程或者線程會收到一個信號SIGIO,這時候就會調用信號處理函數,在信號處理函數中調用IO函數操作數據,完成之後通知用戶:

wKiom1dCjUPhFfEdAAA9vVkWRwc352.png

  1. 用戶程序中事先註冊好一個對於SIGIO的信號處理函數;

  2. 當數據準備完畢的時候會向用戶進程或者線程發送一個SIGIO信號,這時信號就會被捕捉;

  3. 捕捉信號之後就會執行用戶自定義的一個信號處理函數,並且在函數中調用系統函數去獲取數據;

  4. 獲取數據同樣會將數據進行拷貝到用戶空間,這時進程或者線程仍然會被阻塞;

  5. 當數據準備完畢同樣會通知用戶,之後就可以進行數據的處理了;


5. 異步IO模型

    對於異步的IO模型來說,數據的等待和搬遷都不由當前的進程或者線程來處理,調用相應系統函數之後就會直接返回繼續執行,因此當前用戶程並不會被阻塞,當數據已經在用戶空間準備就緒之後會以狀態、通知或者回調來告訴用戶可以進行數據的處理了:

wKioL1dCkeaCpGXuAAARbVOCoR4737.png

  1. 用戶程序調用aio_read函數,告訴內核描述字、緩衝區指針、緩衝區大小、文件偏移以及通知的方式,之後便立即返回;

  2. 這時內核相應的數據操作組件會進行數據的等待和搬遷,期間用戶程序並不受影響繼續執行;

  3. 當數據都已經在用戶空間準備就緒之後就會通過在函數中預留的通知方式來通知用戶程序處理數據;



    從上面的分析中不難發現,在對於數據的獲取過程中都是進行了兩個主要的部分:數據的等待和數據的搬遷;除此相同點之外,下面就總結一下各種IO模型的區別:

wKioL1dCm2vhdsMpAAAteL_WMKA853.png

   從上面的比較可以發現:前四種IO模型也就是阻塞IO、非阻塞IO、IO複用和信號驅動IO模型都是同步的,只有最後一種是異步的異步IO模型;默認情況下所創建出來的socket都是以阻塞的形式,比如網絡通信中的recvfrom和sendto,或者read和write等函數都是以阻塞的方式來實現的。




《完》

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