文章首發於公眾號「陳樹義」及個人部落格 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 模型的分享,今天就聊到這兒。
謝謝大家的閱讀。如果文章對你有幫助,歡迎評論轉發點贊三連,我們下次見~