詳解Unix5種IO模型

定義:

Unix網絡編程對IO模型進行了分類,共分爲5類,要在Unix系統的前提下才有效。
5種IO模型:

  • 阻塞IO。
  • 非阻塞IO。
  • IO多路複用。
  • 信號驅動。
  • 異步IO。

這些IO模型的改動的目的是爲了提高服務器能夠並行處理的連接數,而不是提高程序的執行性能。

前提:要搞懂阻塞、非阻塞、同步、異步。
阻塞、非阻塞、同步、異步可以看徹底搞懂阻塞、非阻塞、同步、異步

五種IO模型詳細分析:

阻塞IO:

定義:進程在進行IO操作時會掛起,會一直阻塞到內核緩衝區數據準備好並複製到用戶緩衝區之後。
例子:老王去釣魚,把帶有魚餌的釣竿放進水裏後就做在河邊一直盯着,啥也不幹,等到魚上鉤才把魚釣上來放進桶裏。期間什麼也幹不了。
在這裏插入圖片描述
流程解釋:

  1. 用戶進程需要進行IO操作時,會進行一次系統調用,進入到內核態,此時用戶進程被掛起。處於阻塞狀態。此時進程不會再佔用cpu資源。
  2. 內核進行數據的準備,把需要的數據填充到內核緩衝區。
  3. 內核緩衝區數據填充完畢,把數據從內核緩衝區複製到用戶緩衝區。
  4. 數據複製完畢,返回,從內核態從新切換到用戶態,進程進入就緒狀態等待cpu執行。

總結:

  • 用戶進程從進行系統調用進入內核態後,會一直掛起,阻塞到數據從內核緩衝區複製到用戶緩衝區完畢。期間不會消耗cpu資源。
  • 適用併發量小的網絡應用開發。

非阻塞IO:

定義:用戶進程在進行IO操作後,不會被掛起,還是會繼續執行邏輯。
例子:老王去釣魚,把帶有魚餌的釣竿放進水裏後,期間可以玩手機,看微信,只是要不斷地把眼睛看向合理,看魚有沒有上鉤,沒有上鉤就繼續玩手機,有就收杆,把魚放進桶裏。
在這裏插入圖片描述
流程說明:

  1. 用戶進程需要進行IO操作時,會進行一次系統調用,進入到內核態,如果數據沒有準備好,立即返回EWOULDBLOCK。此時不會造成進程阻塞。進程還可以繼續處理其他事情。
  2. 進程會輪詢查看內核數據是否準備好,如果沒有準備好,就繼續立即返回EWOULDBLOCK,不阻塞進程。
  3. 輪詢到數據準備好了後,進行數據複製,從內核緩衝區複製到用戶緩衝區。在此期間進程會掛起,處於阻塞狀態,知道數據複製完成。
  4. 數據複製完畢後返回,進程轉爲就緒態,等待cpu調度。

總結:

  • 非阻塞IO因爲要用輪詢代替了阻塞,使得進程在內核數據準備期間不會阻塞,可以執行其他事情,但是因爲要輪詢,所以會對CPU資源造成較大的消耗。
  • 在進行內核態往用戶態數據複製過程中,進程還是會處於阻塞狀態的。
  • 雖然非阻塞IO可以使得進程在IO內核數據準備期間不阻塞,可以執行其他事情,但是,由於輪詢需要消耗較大的cpu資源,所以會使得服務端處理和響應請求會有較大的延時。
  • 適用併發量較小、且不需要及時響應的網絡應用開發。

IO多路複用:

IO多路複用有基於select/poll的對路複用,也有基於epoll的多路複用。

基於select/poll的多路複用:

多個網絡IO連接可以註冊到一個復路器select上,然後由一個進程或者線程調用該復路器,調用該復路器會使得進程或者線程掛起,處於阻塞狀態,內核會輪詢監視該復路器上的每一個連接,一旦有一個連接的數據準備好了,該select會返回,然後進程或者線程退出阻塞狀態,然後該進程或者線程會進行系統調用,把內核緩衝區的數據複製到用戶緩衝區。

例子:老王去河邊釣魚,不過他有點貪心,一次性使用多個魚竿釣魚(假設10個),然後把十個魚竿放進合理,然後就眼睛不斷從左往右循環看每一根魚竿是否有魚上鉤,其中一根魚竿上鉤了,就把魚釣起放進桶裏。

在這裏插入圖片描述
流程說明:

  1. 連接到服務進程上的多個套接字連接會註冊到復路器select上。
  2. 進程調用select系統調用。進入內核態,應用進程掛起,內核會對select上的連接進行監視輪詢,監視連接的數據是否準備好。此間進程會處於阻塞狀態。
  3. 一旦select複用器上的一個或者多個連接數據準備好了,select會返回,然後進程會取消阻塞狀態並再發起系統調用recvfrom把內核緩衝區的數據複製到用戶緩衝區,此時進程又會被掛起,此時的系統調用recvfrom時內核緩衝區數據必定是準備好的。
  4. 數據複製完成返回。

與阻塞IO的區別:

  • 基於select/poll的IO複用與阻塞IO類似,只不過阻塞IO是一直阻塞在recvfrom系統調用等待數據準備好並複製到緩衝區,而IO複用會先阻塞在select或者poll系統調用,監視某一個連接數據準備好的再返回,然後調用recvfrom系統調用進行數據複製,只是此時的recvfrom不再需要等待數據準備,可以直接複製。時間較短。主要阻塞在select/poll系統調用上。
  • 基於select/poll的IO複用實際上比阻塞IO多了一個步驟,多進行了一次系統調用,只不過IO複用的優勢不是在於處於一個連接更快,而是可以處理更多的連接。
  • 在處理連接數不高的情況下,基於select/poll的IO多路複用的服務器不一定會比多線程+阻塞IO的服務器性能高,可能延時會更大。

總結:

  • 基於select或者poll模型的IO多路複用基本是一樣的只是基於select的模型默認能同時接收的連接數是1024個,因爲一個進程默認最多打開1024個fd文件描述符。而基於poll模型的IO多路複用就沒有限制,因爲是基於鏈表來存儲的。
  • 基於select/poll的IO多路複用也不能設置太大的連接數,因爲監視複用器的連接數據是否準備好是採用循環進行無差別的監視,時間複雜度爲O(n),如果連接數太大的話,循環監視一次的時間會相對長,反而會降低監視的效率。

基於epoll的IO多路複用:

回顧基於select/poll的IO多路複用缺點:
  • select的模型默認能同時接收的連接數是1024個,因爲一個進程默認最多打開1024個fd文件描述符。而基於poll模型的IO多路複用就沒有限制。
  • 對復路器上的連接的監視輪詢是線性時間複雜度O(n),也就是說隨着連接數的增加,對復路器的監視效率會降低。
基於epoll的IO多路複用的改進:
  • 一個進程打開的fd連接文件描述符沒有限制。會限制於內存大小,1GB內存大概可以打開10w個。
  • 利用每個文件描述符fd上的callback函數來實現異步回調,省略了對復路器上的連接監視輪詢的開銷。時間複雜度O(1),就不會隨着連接數的增多而降低。

例子:老王去河邊釣魚,不過他有點貪心,一次性使用多個魚竿釣魚(假設10個)(不過老王這從使用的是升級版的魚竿,每根魚竿上綁着一個小鈴鐺,當有魚上鉤時鈴鐺會響),然後把十個魚竿放進河裏,因爲使用了升級版魚竿,使用老王不用從左到右盯着魚竿,但是也什麼也幹不了,只能那發呆。等到某一根魚竿有魚上鉤,鈴鐺就會響,然後老王就把那根魚竿收杆,把魚放進桶裏。

流程:

  1. 連接到服務進程上的多個套接字連接會註冊到復路器上。
  2. 進程調用epoll系統調用。進入內核態,應用進程掛起。
  3. 一旦複用器上的某個連接數據準備好了,就會通過該連接套接字描述符fd上的回調函數通知應用進程並,然後進程會取消阻塞狀態並再發起系統調用recvfrom把內核緩衝區的數據複製到用戶緩衝區,此時進程又會被掛起,此時的系統調用recvfrom時內核緩衝區數據必定是準備好的。
  4. 數據複製完成返回。
基於select/poll的IO複用與基於epoll的IO複用對比:
  • 基於select的IO複用會有最大連接數限制,基於poll的IO複用沒有限制,但是這兩個都會隨着連接數的增加而性能線性降低。而基於epoll的IO複用採用的是異步回調方式通知用戶進程,不會隨着連接數增加而性能線性降低。
  • 在select poll epoll都會阻塞進程。之後的recvfrom系統調用也會阻塞進程,只是調用recvfrom代表着數據已經準備好了,可以直接複製,所以IO複用的阻塞主要集中在select poll epoll上。

信號驅動:

老王去河邊釣魚,用的升級版魚竿,放下魚竿後他可以幹其他事,玩遊戲,刷微博等,等聽到鈴鐺響了之後,就把魚放到桶裏。
在這裏插入圖片描述
流程說明:

  1. 在Socket連接上安裝一個信號處理函數,然後進程調用sigaction系統調用,但是立即返回,進程不用阻塞。繼續執行。
  2. 當某個Socket的數據準備好了之後,進程會收到一個SIGIO信號,可以在信號處理函數中調用recvfrom進行數據的複製。複製過程中進程阻塞。
  3. 複製完成返回。

異步IO:

異步IO是最快的。

例子:老王去河邊釣魚,用的終極版魚竿(自動放進桶裏並播放響鈴),放下魚竿後他可以幹其他事,玩遊戲,刷微博等,魚上鉤了以後,終極版魚竿會自動收杆並把魚放進桶裏後,響鈴會響,通知老王把魚煮了。
在這裏插入圖片描述
流程說明:

  1. 應用進程接收到IO連接,發起一次aio_read系統調用,然後立即返回,進程不阻塞,可以繼續執行。
  2. 等待數據準備好。
  3. 數據準備好了以後,內核直接把數據從內核緩衝區複製到用戶緩衝區,而無需用戶進程發起系統調用再進行復制。
  4. 數據複製完以後返回指定信號給用戶進程,此時數據已經在用戶的緩衝區了,所以用戶進程可以直接在用戶緩衝區拿數據處理。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章