Java 網路IO模型簡介
之前自學以及在公司裡的一年多都沒有接觸過網路IO相關的知識,六月份二面的時候有位面試官問了我一些有關網路IO的相關問題,結果一臉懵逼。趁著現在有空,正好入門一下。
基礎概念
正式開始之前,需要鋪墊一些基本概念,以免接下來看到一臉懵逼。
我們都知道,在作業系統中,CPU
負責執行指令,這些指令有些來自應用程式,有些是底層系統的自呼叫。有些指令是非常危險的,如清除記憶體,網路連線等等,如果錯誤呼叫的話有可能導致系統崩潰。因而CPU
將指令分為特權指令和非特權指令,對於某些特定的指令,只需要作業系統及其相關模組進行呼叫。因而,根據這個特點,作業系統內部也劃分出了核心態和使用者態。
核心態
核心態擁有完全的底層資源控制權限,可以執行任何的CPU
使用者態
使用者程式是執行在作業系統之上,這些程式執行時稱之為使用者態,使用者態下不能直接訪問底層硬體和記憶體地址,只能通過委託系統呼叫的方式來訪問底層硬體和記憶體。
使用者態到核心態如何切換
從使用者態切換到核心態有三種方式:
- 系統呼叫:這是使用者態主動要求切換到核心態的一種方式。使用者程序通過系統呼叫申請使用作業系統提供的某些服務以便完成工作,比如,呼叫
fork()
指令實際上就是執行了一個建立新程序的系統呼叫。系統呼叫的機制其核心在於**使用了作業系統為使用者特別開放的一箇中斷來實現的,例如Linux
的int 80h
中斷; - 外設中斷
CPU
發出相應的中斷訊號。這時CPU
會暫停執行下一條即將要執行的指令轉而去執行與中斷訊號對應的處理程式。如果先前執行的是使用者態下的指令,那麼這個切換過程就是使用者態轉為核心態。比如硬碟讀寫操作完成,系統會切換到硬碟讀寫的中斷處理程式中執行後續操作; - 異常:當
CPU
在執行執行處於使用者態的程式時,發生了一些不可知的異常,這個時候就會觸發由當前執行進行切換到處理此異常的核心相關程式中,也就是轉到了核心態,比如缺頁異常;
這三種是使用者態切換到核心態的主要方式,系統呼叫是主動的,後面兩種是被動的。
Linux
的整體架構圖如下所示:
同步/非同步
同步/非同步關注的是訊息通訊機制。
同步:所謂同步,就是在發出一個功能呼叫時,在沒有得到結果之前,該呼叫就不返回。等前一件做完了才能做下一件事。
非同步:非同步的概念和同步相對。當一個非同步過程呼叫發出後,呼叫者若不能立刻得到結果,此時可以直接返回然後執行其他任務,等到獲得了結果之後通過狀態、通知或者回調等手段通知呼叫者。
同步、非同步一般發生在不同的執行緒/程序之間,如Thread1
和Thread2
是同步執行還是非同步執行的。
阻塞和非阻塞
阻塞和非阻塞關注的是程式在等待呼叫結果時的狀態。
阻塞: 阻塞呼叫是指呼叫返回之前,當前執行緒會被掛起,只有當呼叫得到結果後才返回。
非阻塞:與阻塞相反,非阻塞呼叫是指在不能立即得到結果之前,該函式不會將當前執行緒阻塞,而是立即返回。
五種 IO 模型
IO
一般分為磁碟IO和網路IO,這裡我們主要關注網路IO。一次完整的網路IO
過程如下所示:
從上圖可以看出,資料無論從網絡卡到使用者空間還是從使用者空間到網絡卡都需要經過核心。
阻塞IO模型
當應用程式呼叫一個 IO
函式,其底層會委託作業系統的recvfrom()
去完成,當資料還沒有準備好時,revfrom
會一直阻塞,等待資料準備好。當資料準備好後,從核心拷貝到使用者空間,recvfrom
返回成功,IO
函式呼叫完成。過程如下所示:
阻塞IO
模型的優點是程式設計簡單,但缺點是需要配合大量執行緒使用。應用程序沒接收一個連線,就需要為此連線建立一個執行緒來處理該連線上的讀寫任務。
非阻塞IO模型
呼叫程序在等待資料的過程中不會被阻塞,而是會不斷地輪詢檢視資料有沒有準備好。當資料準備好後,將資料從核心空間拷貝到使用者空間,完成IO
函式的呼叫。等待資料的過程是非阻塞的,但資料拷貝時仍是阻塞的。過程如下所示:
非阻塞io
的優點在於可以實現使用一個執行緒同時處理多個連線的需求,減少執行緒的大量使用。缺點在於要不斷地去輪詢檢查資料是否準備好,比較耗費CPU
。
IO複用模型
為了解決非阻塞IO
不斷輪詢導致CPU
佔用升高的問題,出現了IO
複用模型。IO
複用中,使用其他執行緒幫助去檢查多個執行緒資料的完成情況,提高效率。
Linux
中提供了select
、poll
和epoll
三種方式來實現IO
複用。一個執行緒可以對多個IO
埠進行監聽,當有讀寫事件產生時會分發到具體的執行緒進行處理。過程如下所示:
IO
複用只需要阻塞在select
,poll
或者epoll
,可以同時處理和管理多個連線。缺點是當 select
、poll
或者epoll
管理的連線數過少時,這種模型將退化成阻塞 IO
模型。並且還多了一次系統呼叫:一次 select
、poll
或者epoll
一次 recvfrom
。
訊號驅動IO模型
應用程式可以建立一個訊號驅動程式SIGIO
,當資料沒有處理好時,應用程式繼續執行,不會被阻塞。當資料準備好之後,作業系統嚮應用程式傳送訊號,之後訊號驅動程式就會執行,在訊號處理函式中呼叫 IO
函式處理資料。過程如下所示:
訊號驅動IO
模型的優點在於非阻塞,缺點在於序列處理訊號驅動程式,當前一個SIGIO
沒有被處理的情況下,後一個訊號也不能被處理。在訊號量大的時候會導致後面的訊號不能被及時感知。
非同步IO模型
相比於同步IO
,非同步IO
不是順序執行的。應用程序在執行aio_read
系統呼叫之後,無論資料是否準備好,都會直接返回給使用者程序,然後應用程序可以去做別的事情。當資料準備好之後,核心直接複製資料給使用者程序,然後核心向程序傳送通知。過程如下:
訊號驅動IO
模型中核心通知應用程序資料何時準備好,而在非同步IO
模型中核心將資料複製完成之後告知應用程序IO
操作已完成。
在非同步IO
模型中,應用程序呼叫aio_read
以及資料被拷貝到使用者空間這兩個過程都是非阻塞的。
總結
IO
模型公有五種,前四種模型區別在於第一部分,即系統呼叫,但是第二部分都是一樣的,即將資料從核心空間拷貝到使用者空間這個過程,程序阻塞於redvfrom
的呼叫。而最後一種,非同步IO
模型,在系統呼叫和資料拷貝過程都是非阻塞的。