1. 程式人生 > >京東數科二面:常見的 IO 模型有哪些?Java 中的 BIO、NIO、AIO 有啥區別?

京東數科二面:常見的 IO 模型有哪些?Java 中的 BIO、NIO、AIO 有啥區別?

IO 模型這塊確實挺難理解的,需要太多計算機底層知識。寫這篇文章用了挺久,就非常希望能把我所知道的講出來吧!希望朋友們能有收貨!為了寫這篇文章,還翻看了一下《UNIX 網路程式設計》這本書,太難了,我滴乖乖!心痛~ _個人能力有限。如果文章有任何需要補充/完善/修改的地方,歡迎在評論區指出,共同進步!_ ## 前言 I/O 一直是很多小夥伴難以理解的一個知識點,這篇文章我會將我所理解的 I/O 講給你聽,希望可以對你有所幫助。 ## I/O ### 何為 I/O? I/O(**I**nput/**O**utpu) 即**輸入/輸出** 。 **我們先從計算機結構的角度來解讀一下 I/O。** 根據馮.諾依曼結構,計算機結構分為 5 大部分:運算器、控制器、儲存器、輸入裝置、輸出裝置。 ![馮諾依曼體系結構](https://img-blog.csdnimg.cn/20190624122126398.jpeg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9pcy1jbG91ZC5ibG9nLmNzZG4ubmV0,size_16,color_FFFFFF,t_70) 輸入裝置(比如鍵盤)和輸出裝置(比如滑鼠)都屬於外部裝置。網絡卡、硬碟這種既可以屬於輸入裝置,也可以屬於輸出裝置。 輸入裝置向計算機輸入資料,輸出裝置接收計算機輸出的資料。 **從計算機結構的視角來看的話, 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/O**、**I/O 多路複用**、**訊號驅動 I/O** 和**非同步 I/O**。 這也是我們經常提到的 5 種 IO 模型。 ## Java 中 3 種常見 IO 模型 ### BIO (Blocking I/O) **BIO 屬於同步阻塞 IO 模型** 。 同步阻塞 IO 模型中,應用程式發起 read 呼叫後,會一直阻塞,直到在核心把資料拷貝到使用者空間。 ![圖源:《深入拆解Tomcat & Jetty》](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6a9e704af49b4380bb686f0c96d33b81~tplv-k3u1fbpfcp-watermark.image) 在客戶端連線數量不高的情況下,是沒問題的。但是,當面對十萬甚至百萬級連線的時候,傳統的 BIO 模型是無能為力的。因此,我們需要一種更高效的 I/O 處理模型來應對更高的併發量。 ### NIO (Non-blocking/New I/O) Java 中的 NIO 於 Java 1.4 中引入,對應 `java.nio` 包,提供了 `Channel` , `Selector`,`Buffer` 等抽象。NIO 中的 N 可以理解為 Non-blocking,不單純是 New。它支援面向緩衝的,基於通道的 I/O 操作方法。 對於高負載、高併發的(網路)應用,應使用 NIO 。 Java 中的 NIO 可以看作是 **I/O 多路複用模型**。也有很多人認為,Java 中的 NIO 屬於同步非阻塞 IO 模型。 跟著我的思路往下看看,相信你會得到答案! 我們先來看看 **同步非阻塞 IO 模型**。 ![圖源:《深入拆解Tomcat & Jetty》](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bb174e22dbe04bb79fe3fc126aed0c61~tplv-k3u1fbpfcp-watermark.image) 同步非阻塞 IO 模型中,應用程式會一直髮起 read 呼叫,等待資料從核心空間拷貝到使用者空間的這段時間裡,執行緒依然是阻塞的,直到在核心把資料拷貝到使用者空間。 相比於同步阻塞 IO 模型,同步非阻塞 IO 模型確實有了很大改進。通過輪詢操作,避免了一直阻塞。 但是,這種 IO 模型同樣存在問題:**應用程式不斷進行 I/O 系統呼叫輪詢資料是否已經準備好的過程是十分消耗 CPU 資源的。** 這個時候,**I/O 多路複用模型** 就上場了。 ![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/88ff862764024c3b8567367df11df6ab~tplv-k3u1fbpfcp-watermark.image) IO 多路複用模型中,執行緒首先發起 select 呼叫,詢問核心資料是否準備就緒,等核心把資料準備好了,使用者執行緒再發起 read 呼叫。read 呼叫的過程(資料從核心空間->使用者空間)還是阻塞的。 > 目前支援 IO 多路複用的系統呼叫,有 select,epoll 等等。select 系統呼叫,是目前幾乎在所有的作業系統上都有支援 > > - **select 呼叫** :核心提供的系統呼叫,它支援一次查詢多個系統呼叫的可用狀態。幾乎所有的作業系統都支援。 > - **epoll 呼叫** :linux 2.6 核心,屬於 select 呼叫的增強版本,優化了 IO 的執行效率。 **IO 多路複用模型,通過減少無效的系統呼叫,減少了對 CPU 資源的消耗。** Java 中的 NIO ,有一個非常重要的**選擇器 ( Selector )** 的概念,也可以被稱為 **多路複用器**。通過它,只需要一個執行緒便可以管理多個客戶端連線。當客戶端資料到了之後,才會為其服務。 ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0f483f2437ce4ecdb180134270a00144~tplv-k3u1fbpfcp-watermark.image) ### AIO (Asynchronous I/O) AIO 也就是 NIO 2。Java 7 中引入了 NIO 的改進版 NIO 2,它是非同步 IO 模型。 非同步 IO 是基於事件和回撥機制實現的,也就是應用操作之後會直接返回,不會堵塞在那裡,當後臺處理完成,作業系統會通知相應的執行緒進行後續的操作。 ![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3077e72a1af049559e81d18205b56fd7~tplv-k3u1fbpfcp-watermark.image) 目前來說 AIO 的應用還不是很廣泛。Netty 之前也嘗試使用過 AIO,不過又放棄了。這是因為,Netty 使用了 AIO 之後,在 Linux 系統上的效能並沒有多少提升。 最後,來一張圖,簡單總結一下 Java 中的 BIO、NIO、AIO。 ![](https://images.xiaozhuanlan.com/photo/2020/33b193457c928ae02217480f994814b6.png) ## 參考 - 《深入拆解 Tomcat & Jetty》 - 如何完成一次 IO:[https://llc687.top/post/如何完成一次-io/](https://llc687.top/post/如何完成一次-io/) - 程式設計師應該這樣理解 IO:[https://www.jianshu.com/p/fa7bdc4f3de7](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 節 I