常見的 IO 模型有哪些?Java 中 BIO、NIO、AIO 的區別?

IO 模型這塊確實挺難理解的,需要太多計算機底層知識。寫這篇文章用了挺久,就非常希望能把我所知道的講出來吧!希望朋友們能有收貨!爲了寫這篇文章,還翻看了一下《UNIX 網絡編程》這本書,太難了,我滴乖乖!心痛~

個人能力有限。如果文章有任何需要補充/完善/修改的地方,歡迎在評論區指出,共同進步!

相關閱讀(原創):淘寶一面:“說一下 Spring Boot 自動裝配原理唄?” 

前言

I/O 一直是很多小夥伴難以理解的一個知識點,這篇文章我會將我所理解的 I/O 講給你聽,希望可以對你有所幫助。

I/O

何爲 I/O?

I/O(Input/Outpu) 即輸入/輸出

我們先從計算機結構的角度來解讀一下 I/O。

根據馮.諾依曼結構,計算機結構分爲 5 大部分:運算器、控制器、存儲器、輸入設備、輸出設備。

馮諾依曼體系結構

輸入設備(比如鍵盤)和輸出設備(比如鼠標)都屬於外部設備。網卡、硬盤這種既可以屬於輸入設備,也可以屬於輸出設備。

輸入設備向計算機輸入數據,輸出設備接收計算機輸出的數據。

從計算機結構的視角來看的話, I/O 描述了計算機系統與外部設備之間通信的過程。

我們再先從應用程序的角度來解讀一下 I/O。

根據大學裏學到的操作系統相關的知識:爲了保證操作系統的穩定性和安全性,一個進程的地址空間劃分爲 用戶空間(User space)內核空間(Kernel space )

像我們平常運行的應用程序都是運行在用戶空間,只有內核空間才能進行系統態級別的資源有關的操作,比如如文件管理、進程通信、內存管理等等。也就是說,我們想要進行 IO 操作,一定是要依賴內核空間的能力。

並且,用戶空間的程序不能直接訪問內核空間。

當想要執行 IO 操作時,由於沒有執行這些操作的權限,只能發起系統調用請求操作系統幫忙完成。

因此,用戶進程想要執行 IO 操作的話,必須通過 系統調用 來間接訪問內核空間

我們在平常開發過程中接觸最多的就是 磁盤 IO(讀寫文件)網絡 IO(網絡請求和相應)

從應用程序的視角來看的話,我們的應用程序對操作系統的內核發起 IO 調用(系統調用),操作系統負責的內核執行具體的 IO 操作。也就是說,我們的應用程序實際上只是發起了 IO 操作的調用而已,具體 IO 的執行是由操作系統的內核來完成的。

當應用程序發起 I/O 調用後,會經歷兩個步驟:

  1. 內核等待 I/O 設備準備好數據
  2. 內核將數據從內核空間拷貝到用戶空間。

有哪些常見的 IO 模型?

UNIX 系統下, IO 模型一共有 5 種:同步阻塞 I/O同步非阻塞 I/OI/O 多路複用信號驅動 I/O異步 I/O

這也是我們經常提到的 5 種 IO 模型。

Java 中 3 種常見 IO 模型

BIO (Blocking I/O)

BIO 屬於同步阻塞 IO 模型

同步阻塞 IO 模型中,應用程序發起 read 調用後,會一直阻塞,直到在內核把數據拷貝到用戶空間。

圖源:《深入拆解Tomcat & Jetty》

在客戶端連接數量不高的情況下,是沒問題的。但是,當面對十萬甚至百萬級連接的時候,傳統的 BIO 模型是無能爲力的。因此,我們需要一種更高效的 I/O 處理模型來應對更高的併發量。

NIO (Non-blocking/New I/O)

Java 中的 NIO 於 Java 1.4 中引入,對應 java.nio 包,提供了 Channel , SelectorBuffer 等抽象。NIO 中的 N 可以理解爲 Non-blocking,不單純是 New。它支持面向緩衝的,基於通道的 I/O 操作方法。對於高負載、高併發的(網絡)應用,應使用 NIO 。

Java 中的 NIO 可以看作是 I/O 多路複用模型。也有很多人認爲,Java 中的 NIO 屬於同步非阻塞 IO 模型。

跟着我的思路往下看看,相信你會得到答案!

我們先來看看 同步非阻塞 IO 模型

圖源:《深入拆解Tomcat & Jetty》

同步非阻塞 IO 模型中,應用程序會一直髮起 read 調用,等待數據從內核空間拷貝到用戶空間的這段時間裏,線程依然是阻塞的,直到在內核把數據拷貝到用戶空間。

相比於同步阻塞 IO 模型,同步非阻塞 IO 模型確實有了很大改進。通過輪詢操作,避免了一直阻塞。

但是,這種 IO 模型同樣存在問題:應用程序不斷進行 I/O 系統調用輪詢數據是否已經準備好的過程是十分消耗 CPU 資源的。

這個時候,I/O 多路複用模型 就上場了。

IO 多路複用模型中,線程首先發起 select 調用,詢問內核數據是否準備就緒,等內核把數據準備好了,用戶線程再發起 read 調用。read 調用的過程(數據從內核空間->用戶空間)還是阻塞的。

目前支持 IO 多路複用的系統調用,有 select,epoll 等等。select 系統調用,是目前幾乎在所有的操作系統上都有支持

  • select 調用 :內核提供的系統調用,它支持一次查詢多個系統調用的可用狀態。幾乎所有的操作系統都支持。
  • epoll 調用 :linux 2.6 內核,屬於 select 調用的增強版本,優化了 IO 的執行效率。

IO 多路複用模型,通過減少無效的系統調用,減少了對 CPU 資源的消耗。

Java 中的 NIO ,有一個非常重要的選擇器 ( Selector ) 的概念,也可以被稱爲 多路複用器。通過它,只需要一個線程便可以管理多個客戶端連接。當客戶端數據到了之後,纔會爲其服務。

AIO (Asynchronous I/O)

AIO 也就是 NIO 2。Java 7 中引入了 NIO 的改進版 NIO 2,它是異步 IO 模型。

異步 IO 是基於事件和回調機制實現的,也就是應用操作之後會直接返回,不會堵塞在那裏,當後臺處理完成,操作系統會通知相應的線程進行後續的操作。

目前來說 AIO 的應用還不是很廣泛。Netty 之前也嘗試使用過 AIO,不過又放棄了。這是因爲,Netty 使用了 AIO 之後,在 Linux 系統上的性能並沒有多少提升。

最後,來一張圖,簡單總結一下 Java 中的 BIO、NIO、AIO。

參考

  • 《深入拆解 Tomcat & Jetty》
  • 如何完成一次 IO:https://llc687.top/post/如何完成一次-io/
  • 程序員應該這樣理解 IO:https://www.jianshu.com/p/fa7bdc4f3de7
  • 10 分鐘看懂, Java NIO 底層原理:https://www.cnblogs.com/crazymakercircle/p/10225159.html
  • IO 模型知多少 | 理論篇:https://www.cnblogs.com/sheng-jie/p/how-much-you-know-about-io-models.html
  • 《UNIX 網絡編程 卷 1;套接字聯網 API 》6.2 節 IO 模型

本文分享自微信公衆號 - 武培軒(wupeixuan404)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

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