一文了解Linux 网络 I/O 模型
目錄
1 什么是I/O
2 同步IO模型
2.1 阻塞IO模型
2.2 非阻塞IO模型
2.3 信號驅動IO模型
2.4 IO復用模型
2.5 小結
3 異步IO模型
4 五種IO模型對比
1 什么是I/O
IO中的阻塞、非阻塞、同步、異步概念分析詳解?
通過上面這篇文章你可以知道同步、異步、阻塞、非阻塞這些概念,并且可以了解到java中I/O編程的三種模型,阻塞IO(BIO)、非阻塞IO(NIO)和異步IO(AIO)。
Java中提供的IO有關的API,在文件處理的時候,其實依賴操作系統層面的IO操作實現的。比如在Linux 2.6以后,Java中NIO和AIO都是通過epoll來實現的,而在Windows上,AIO是通過IOCP來實現的。可以把Java中的BIO、NIO和AIO理解為是Java語言對操作系統的各種IO模型的封裝。程序員在使用這些API的時候,不需要關心操作系統層面的知識,也不需要根據不同操作系統編寫不同的代碼。只需要使用Java的API就可以了。
網絡IO的本質是socket的讀取,socket在linux系統被抽象為流,IO可以理解為對流的操作。?對于一次IO訪問(以read舉例),數據會先被拷貝到操作系統內核的緩沖區中,然后才會從操作系統內核的緩沖區拷貝到應用程序的地址空間。所以說,
當一個read操作發生時,它會經歷兩個階段:
-
等待數據準備 (Waiting for the data to be ready)。
-
將數據從內核拷貝到進程中 (Copying the data from the kernel to the process)。
對于socket流而言,
-
第一步:通常涉及等待網絡上的數據分組到達,然后被復制到內核的某個緩沖區。
-
第二步:把數據從內核緩沖區復制到應用進程緩沖區。
網絡應用需要處理的無非就是兩大類問題,網絡IO,數據計算。相對于后者,網絡IO的延遲,給應用帶來的性能瓶頸大于后者。如果要想提高IO效率,需要將等的時間降低。
可以通過釣魚的例子來理解下:
是在魚塘里面的,我們的釣魚動作的最終結束標志是魚從魚塘中被我們釣上來,放入魚簍中。
這里面的魚塘就可以映射成磁盤,中間過渡的魚鉤可以映射成內核空間,最終放魚的魚簍可以映射成用戶空間。一次完整的釣魚(IO)操作,是魚(文件)從魚塘(硬盤)中轉移(拷貝)到魚簍(用戶空間)的過程
在Linux(UNIX)操作系統中,共有五種IO模型,分別是:阻塞IO模型、非阻塞IO模型、IO復用模型、信號驅動IO模型以及異步IO模型。
2 同步IO模型
2.1 阻塞IO模型
我們釣魚的時候,有一種方式比較愜意,比較輕松,那就是我們坐在魚竿面前,這個過程中我們什么也不做,雙手一直把著魚竿,就靜靜的等著魚兒咬鉤。一旦手上感受到魚的力道,就把魚釣起來放入魚簍中。然后再釣下一條魚。
映射到Linux操作系統中,這就是一種最簡單的IO模型,即阻塞IO。 阻塞 I/O 是最簡單的 I/O 模型,一般表現為進程或線程等待某個條件,如果條件不滿足,則一直等下去。條件滿足,則進行下一步操作。?
應用進程通過系統調用 recvfrom 接收數據,但由于內核還未準備好數據報,應用進程就會阻塞住,直到內核準備好數據報,recvfrom 完成數據報復制工作,應用進程才能結束阻塞狀態。
這種釣魚方式相對來說比較簡單,對于釣魚的人來說,不需要什么特制的魚竿,拿一根夠長的木棍就可以悠閑的開始釣魚了(實現簡單)。缺點就是比較耗費時間,比較適合那種對魚的需求量小的情況(并發低,時效性要求低)。
BIO模型最大的問題就是當客戶端并發訪問量增加后,每個客戶端的請求服務端都需要一個對應的線程來處理,客戶端并發數:服務端線程=1:1 ,Java中的線程是比較寶貴的系統資源,線程數量快速膨脹后,系統的性能將急劇下降,隨著訪問量的繼續增大,系統很可能"崩潰"
2.2 非阻塞IO模型
我們釣魚的時候,在等待魚兒咬鉤的過程中,我們可以做點別的事情,比如玩一把王者榮耀、看一集《延禧攻略》等等。但是,我們要時不時的去看一下魚竿,一旦發現有魚兒上鉤了,就把魚釣上來。
映射到Linux操作系統中,這就是非阻塞的IO模型。應用進程與內核交互,目的未達到之前,不再一味的等著,而是直接返回EWOULDBLOCK錯誤。然后通過輪詢的方式,不停的去問內核數據準備有沒有準備好。如果某一次輪詢發現數據已經準備好了,那就把數據拷貝到用戶空間中。?"非阻塞就是將大的整片時間的阻塞分成N多的小的阻塞, 所以進程不斷地有機會 '被' CPU光顧"
?
應用進程通過 recvfrom 調用不停的去和內核交互,直到內核準備好數據。如果沒有準備好,內核會返回error,應用進程在得到error后,過一段時間再發送recvfrom請求。在兩次發送請求的時間段,進程可以先做別的事情。
這種方式釣魚,和阻塞IO比,所使用的工具沒有什么變化,但是釣魚的時候可以做些其他事情,增加時間的利用率。
優點:能夠在輪詢的間隔時間里處理其他事情(包括提交其他任務,也就是 “后臺” 可以有多個任務在同時執行)。
缺點:任務完成的響應延遲增大了,因為每過一段時間才去輪詢一次,而數據準備可能在兩次輪詢之間的任意時間完成。這會導致整體數據吞吐量的降低。
2.3 信號驅動IO模型
我們釣魚的時候,為了避免自己一遍一遍的去查看魚竿,我們可以給魚竿安裝一個重力報警器。當有魚兒咬鉤的時候立刻報警。然后我們再收到報警后,去把魚釣起來。
映射到Linux操作系統中,這就是信號驅動IO。注冊一個信號處理函數,然后應用進程告訴內核:當數據報準備好的時候,給我發送一個信號。收到信號后,對SIGIO信號進行捕捉,并且調用信號處理函數處理數據并獲取結果。?
?
應用進程預先向內核注冊一個信號處理函數,然后用戶進程返回,并且不阻塞,當內核數據準備就緒時會發送一個信號給進程,用戶進程便在信號處理函數中開始把數據拷貝的用戶空間中。
這種方式釣魚,和前幾種相比,所使用的工具有了一些變化,需要有一些定制(實現復雜)。但是釣魚的人就可以在魚兒咬鉤之前徹底做別的事兒去了。等著報警器響就行了。
2.4 IO復用模型
我們釣魚的時候,為了保證可以最短的時間釣到最多的魚,我們同一時間擺放多個魚竿,同時釣魚。然后哪個魚竿有魚兒咬鉤了,我們就把哪個魚竿上面的魚釣起來。
由于同步非阻塞方式需要不斷主動輪詢,輪詢占據了很大一部分過程,會消耗大量的CPU資源,而 “后臺” 可能有多個任務在同時進行,那么就想到了循環查詢多個任務的完成狀態,只要有任何一個任務完成(數據報準備號),就去處理它。如果輪詢不是使用的進程的用戶態,而是有其他線程幫忙就更好了。那么這就是所謂的 “IO 多路復用”
生產中經常會用到的一種模型(Java的NIO就是基于IO復用模型)
多個進程的IO可以注冊到同一個管道上,這個管道會統一和內核進行交互。當管道中的某一個請求需要的數據準備好之后,進程再把對應的數據拷貝到用戶空間中。
IO多路復用使用了Linux提供的select/poll命令,進程通過將一個或多個fd傳遞給select或poll系統調用,當用戶進程調用該select,select會監聽所有注冊好的fd,如果所有被監聽的fd需要的數據都沒有準備好時,select調用進程會阻塞。當任意一個fd處于就緒狀態后,select調用就會返回,然后進程再通過recvfrom來進行數據拷貝。
select/poll是順序掃描fd是否就緒,而且支持的fd數量有限,因此它的使用受到了一些制約。
Linux還提供一個epoll系統調用,epoll使用基于事件驅動方式代替順序掃描,因此性能更高。當有fd就緒時,立即回調函數rollback。
這里的IO復用模型,并沒有向內核注冊信號處理函數,所以,他并不是非阻塞的。進程在發出select后,要等到select/epoll監聽的所有IO操作中至少有一個需要的數據準備好,才會有返回,并且也需要再次發送請求去進行文件的拷貝。
這種方式的釣魚,通過增加魚竿的方式,可以有效的提升效率。
2.5 小結
上面的阻塞IO模型、非阻塞IO模型、IO復用模型和信號驅動IO模型都是同步的IO模型。原因是因為,無論以上那種模型,真正的數據拷貝過程,都是同步進行的。
信號驅動是內核是在數據準備好之后通知進程,然后進程在通過recvfrom操作進行數據拷貝。我們可以認為數據準備階段是異步的,但是,數據拷貝操作是同步的。所以,整個IO過程也不能認為是異步的。
而IO復用模型中,對于每一個socket,一般都設置成為non-blocking,但是,整個用戶的進程其實是一直被block的。只不過進程是被select這個函數block,而不是被socket IO給block。所以IO多路復用是阻塞在select,epoll這樣的系統調用之上,而沒有阻塞在真正的I/O系統調用如recvfrom之上。
我們把釣魚過程,可以拆分為兩個步驟:1、魚咬鉤(數據準備)。2、把魚釣起來放進魚簍里(數據拷貝)。無論以上提到的哪種釣魚方式,在第二步,都是需要人主動去做的,并不是魚竿自己完成的。所以,這個釣魚過程其實還是同步進行的。
3 異步IO模型
我們釣魚的時候,采用一種高科技釣魚竿,即全自動釣魚竿。可以自動感應魚上鉤,自動收竿,更厲害的可以自動把魚放進魚簍里。然后,通知我們魚已經釣到了,他就繼續去釣下一條魚去了。
映射到Linux操作系統中,這就是異步IO模型。應用進程把IO請求傳給內核后,完全由內核去操作文件拷貝。內核完成相關操作后,會發信號告訴應用進程本次IO已經完成。?
用戶進程發起aio_read操作之后,給內核傳遞描述符fd、緩沖區指針、緩沖區大小等,告訴內核當整個操作完成時,如何通知進程,然后就立刻去做其他事情了。當內核收到aio_read后,會立刻返回,然后內核開始等待數據準備,數據準備好以后,直接把數據拷貝到用戶控件,然后再通知進程本次IO已經完成。
這種方式的釣魚,無疑是最省事兒的。啥都不需要管,只需要交給魚竿就可以了。
4 五種IO模型對比
參考:微信公眾號 漫畫編程
總結
以上是生活随笔為你收集整理的一文了解Linux 网络 I/O 模型的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 手写实现RPC框架基础功能
- 下一篇: linux 其他常用命令