轉自:網絡
前言
在《Unix網絡編程》一書中提到了五種IO模型,分別是:阻塞IO、非阻塞IO、IO復用、信號驅動IO以及異步IO。本篇文章主要介紹IO的基本概念以及阻塞IO、非阻塞IO、IO復用三種模型,供大家參考學習。
一、什么是IO
計算機視角理解IO:
對于計算機而言,任何涉及到計算機核心(CPU和內存)與其他設備間的數據轉移的過程就是IO。IO對于計算機而言有兩層意思:
程序視角理解IO:
現代操作系統將空間劃分為用戶空間和內核空間。

操作系統為了能夠正常平穩地運行下去,它是不會允許應用程序隨意訪問計算機硬件部分,如內存、硬盤、網卡,應用程序必須通過操作系統提供的API來訪問,以達到安全的訪問控制。
總結:IO對于應用程序而言,強調的是通過向內核發起系統調用完成對I/O的間接訪問。
應用程序發起一次IO訪問分為兩個階段:


應用程序發起一個系統調用(recvform),這個時候應用程序會一直阻塞下去,直到內核把數據準備好,并將其從內核復制到用戶空間,復制完成后返回成功提示,這個時候應用程序才會繼續處理數據。
典型的阻塞I/0模型的例子為data=socket.read(),如果內核數據沒有準備就緒,Socket線程就會一直阻塞在read()中等待內核數據就緒。
生活場景:某天,你跟你女朋友去奶茶店買奶茶,點完奶茶后后,由于你們不知道奶茶什么時候才能做好,所以你們就只能一直等著,其他什么事情也不能干。

從上圖可以看出, 非阻塞IO模型需要應用進程不斷地主動詢問內核數據是否已準備好了。
生活場景:你和你女朋友去奶茶店買奶茶,吸取了上一次的教訓,點完奶茶后順便去逛了逛商場。由于你們擔心會錯過取餐,所以你們就每隔一段時間就來問下服務員,你們的奶茶做好了沒有,來來回回好多回,若干次后,終于問到奶茶已經準備好了,然后你們就開心的喝了起來。
四、IO復用模型
非阻塞IO模型需要進程不斷地輪詢發起recvform系統調用,就會有很多的線程不斷調用recvfrom 請求數據,先不說服務器能不能扛得住這么多線程,就算扛得住那么很明顯這種方式是不是太浪費資源了,線程是我們操作系統的寶貴資源,大量的線程用來去讀取數據了,那么就意味著能做其它事情的線程就會少。
例如:你是奶茶店的服務員,每個人點好奶茶后,每隔幾分鐘就來問你一次好了沒有,隨著問的人越來越多,你可能會開始懷疑人生。那么有沒有什么好的解決辦法呢?
答案:不需要所有進程輪詢來發起recvform來查詢數據是否已經準備好了,而是有人幫忙來詢問,這個幫忙的人就是select。
IO復用模型如下所示:

多個進程的IO注冊到一個復用器(select)上,select 會監聽所有注冊進來的IO。如果內核的數據報沒有準備好,調用select 的進程將會被阻塞,而當任一IO在內核緩沖區中有數據,select調用就會返回可讀條件,然后進程再進行recvform系統調用,內核將數據拷貝到用戶空間,注意這個過程是阻塞的。
注意:IO 復用模型在第一個階段和第二個階段其實都有阻塞,第一個階段阻塞于 select 調用,第二個階段阻塞于數據復制。
生活場景:如果每個人都過一會就來問一下奶茶好了沒有,奶茶店的壓力也太大了。于是奶茶店想到了一個辦法,找一個中間人(select)擋在奶茶店前面,顧客(應用進程)詢問那個中間人奶茶好了沒有(對應多個進程的IO注冊到一個復用器(select)上),如果沒有好就讓顧客等待(應用進程阻塞于 select 調用)。中間人持續查看顧客的奶茶是否準備好,如果有一個人的奶茶準備好了就會去通知那個人可以取了(而當任一IO在內核緩沖區中有數據,select調用就會返回可讀條件,然后進程再進行recvform系統調用)。

—— EOF —— 你好,我是飛宇,本碩均于某中流985 CS就讀,先后于百度搜索、字節跳動電商以及攜程等部門擔任Linux C/C++后端研發工程師。
最近跟朋友一起開發了一個新的網站:編程資源網,已經收錄了不少資源(附贈下載地址),如果屏幕前的靚仔/女想要學習編程找不到合適資源的話,不妨來我們的網站看看,歡迎掃碼下方二維碼白嫖~
同時,我也是知乎博主@韓飛宇,日常分享C/C++、計算機學習經驗、工作體會,歡迎點擊此處查看我以前的學習筆記&經驗&分享的資源。
我組建了一些社群一起交流,群里有大牛也有小白,如果你有意可以一起進群交流。
歡迎你添加我的微信,我拉你進技術交流群。此外,我也會經常在微信上分享一些計算機學習經驗以及工作體驗,還有一些內推機會。
加個微信,打開另一扇窗
C/C++98
程序員的那些事兒93
C/C++ · 目錄
上一篇