其實 Linux IO 模型沒那么難


文章首發於公眾號「陳樹義」及個人博客 shuyi.tech,歡迎關注訪問。

博主個人獨立站點開通啦!歡迎點擊訪問:https://shuyi.tech

IO 其實就是 Input 和 Output,在操作系統中就對應數據流的輸入與輸出。這個數據流的兩端,可以是文件,也可以是網絡的一台主機。但無論是文件,還是網絡主機,其傳輸都是類似的,我們今天就以源頭為文件進行說明。

一個文件要從磁盤到我們的內存,需要經過很復雜的操作。首先,需要將數據從硬件讀取出來,然后放入操作系統內核緩沖區,之后再將數據拷貝到程序緩沖區,最后應用程序才能讀取到這個文件。簡單地說,無論什么 IO 模型,其讀取過程總會經歷下面兩個階段:

  • 等待數據到達內核緩沖區
  • 從內核緩沖區拷貝數據到程序緩沖區

文章首發於公眾號「陳樹義」及個人博客 shuyi.tech,歡迎關注訪問。

而我們 Linux 根據這兩個階段的是否阻塞,分成了 5 個經典的 IO 的模型,分別是:

  • 阻塞 IO 模型
  • 非阻塞 IO 模型
  • IO 復用模型
  • 信號驅動 IO 模型
  • 異步 IO 模型

阻塞 IO 模型

阻塞 IO 稱為 Blocking IO,簡稱 BIO。在阻塞 IO 模型中,當進程發起一個讀取文件請求(recvfrom 系統調用)時,如果內核緩存區沒有對應的數據,那么它不會立刻恢復,而是去讀取磁盤數據,當數據讀取完畢后,再返回給進程。此時,第一個階段完成。在這個階段進程是阻塞的,因為它要等待內核將數據讀取到內核緩沖區。

而當進程收到內核的響應之后,進程再把數據從內核緩沖區復制到程序緩沖區,最后完成文件讀取操作。此時,第二個階段完成。在這個階段進程也是阻塞的,因為它要將數據從內核緩沖區拷貝到程序緩沖區。

簡單地說:在阻塞 IO 模型里,從硬件到系統內核、從系統內核到程序空間,都是阻塞的。

非阻塞 IO 模型

在非阻塞 IO 模型下,當一個請求發起讀取文件請求(recvfrom)時,如果內核緩沖區沒有數據,那么內核會讀取文件數據。但此時請求並不會阻塞,而是返回一個錯誤信息(EWOULDBLOCK)告訴進程:數據暫時還沒准備好,你待會兒再試試。

於是進程就不斷地向內核重試,問:數據准備好了沒有,數據准備好了沒有……當內核准備好數據,進程就會收到對應消息,於是第一階段就結束了。非阻塞 IO 中的非阻塞說的就是進程不會阻塞在這里,而是會不斷重試。

雖然說這樣並沒有太大用處,反而會使得 CPU 空轉,但總比之前有了一點進步。在這個階段進程並不是阻塞的。當進程得知內核准備好數據之后,其便會將數據從內核緩沖區拷貝到程序緩沖區。這個階段與阻塞 I/O 模型是完全一樣的,同樣是會導致進程阻塞。

文章首發於公眾號「陳樹義」及個人博客 shuyi.tech,歡迎關注訪問。

簡單地說:在非阻塞 IO 模型里,從硬件到系統內核、從系統內核到程序空間,同樣都是阻塞的。但是其比阻塞 IO 爭氣了一點,並不是站在那里不動,好歹還跑了一下。雖然是在做無用功,但是好歹提高了一丟丟效率。

IO 復用模型

IO 復用之所以叫復用,是因為其能同時操作多個數據流。而前面的 阻塞 IO、非阻塞 IO 同一時間只能操作一個數據流。在 IO 復用模型中,進程監聽多個數據流並阻塞,當任何一個數據流有數據之后,其便會收到內核的響應。此時,第一個階段完成,在這個階段進程其實是阻塞的。

而當收到內核的響應后,進程便會將數據從內核緩沖區復制到程序緩沖區。這個階段與上面兩個模型一模一樣,進程同樣阻塞。

簡單地說:IO 復用模型在第二階段與阻塞 IO 和非阻塞 IO 是完全一致的。但是在第一階段上,其有效率上的巨大提升,其能同時輪詢多個數據流,提高了效率。

信號驅動 IO 模型

信號驅動與前面幾個模型的不同之處就在與信號這個詞。信號驅動 IO 在第一階段,即數據到達內核緩沖區之前,進程是不阻塞的,而是設置一個信號回調。當數據到達內核緩沖區之后,內核調用程序的回調。通過這種方式,信號驅動 IO 下的進程就可以不阻塞,可以去做其他事情了。

而當進程收到信號,進程再將數據從內核緩沖區復制到程序緩沖區。這個過程與上面幾個是完全一樣的,同樣也是阻塞的。

信號驅動 IO 可以說是 IO 讀取的一個里程碑,其真正實現了異步讀取數據。信號驅動 IO 其二個階段,與上面幾個是一樣的。但是其在第一個階段做到了真正的異步。信號驅動 IO 在第一階段,其去請求內核讀取數據,這時候其不會阻塞,也不會去尋輪,而是設置一個信號回調。 當數據完全拷貝到系統內核時,系統發出 SIGIO 信號,通知進程去進行第二階段,將數據拷貝到程序緩沖區。

異步 IO 模型

異步 IO 相比前面幾個流程,真正做到了完全非阻塞。無論是在第一階段,還是在第二階段都是非阻塞。與信號驅動 IO 類似,異步 IO 模型通過信號回調的方式,在第一個階段實現了進程的非阻塞。而當數據到達內核緩沖區之后,進程便會收到通知。

而當進程收到通知之后,進程再次將數據從內核緩沖區復制到進程緩沖區,但這時進程並不等待,而是同樣設置一個信號回調。當復制完成后,進程收到通知,再進行相應的處理。

異步 IO 與信號驅動 IO 相比,做得更加徹底了!

異步 IO 不僅僅是在第一階段實現了信號回調,其也在第二階段實現了信號回調,從而完全實現了異步 IO 操作。

文章首發於公眾號「陳樹義」及個人博客 shuyi.tech,歡迎關注訪問。

總結

我們回顧一下這 5 種 IO 模型:

  • 阻塞 IO 模型:硬件到系統內核,阻塞。系統內核到程序空間,阻塞。
  • 非阻塞 IO 模型:硬件到系統內核,輪詢阻塞。系統內核到程序空間,阻塞。
  • 復用 IO 模型:硬件到系統內核,多流輪詢阻塞。系統內核到程序空間,阻塞。
  • 信號驅動 IO 模型:硬件到系統內核,信號回調不阻塞。系統內核到程序空間,阻塞。
  • 異步 IO 模型:硬件到系統內核,信號回調不阻塞。系統內核到程序空間,信號回調不阻塞。

從上面的 5 種 IO 模型,我們可以看出,真正實現異步非阻塞的只有異步 IO 這種模型,而其他四種都是同步性 IO。因為在第二階段:從內核緩沖區復制到進程緩沖區的時候,不可能干其他事情。

好了,關於 Linux IO 模型的分享,今天就聊到這兒。

謝謝大家的閱讀。如果文章對你有幫助,歡迎評論轉發點贊三連,我們下次見~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM