網絡 IO 模型簡單介紹

一、同步阻塞 IO(BIO)

當用戶線程調用了 read 系統調用,內核(kernel)就開始了 IO 的第一個階段:準備數據。很多時候,數據在一開始還沒有到達(比如,還沒有收到一個完整的Socket數據包),這個時候 kernel 就要等待足夠的數據到來。

當 kernel 一直等到數據準備好了,它就會將數據從 kernel 內核緩衝區,拷貝到用戶緩衝區(用戶內存),然後 kernel 返回結果。

從用戶線程 read 系統調用開始,用戶線程就進入阻塞狀態,一直到 kernel 返回結果後,用戶線程才解除 block 的狀態,重新運行起來。

BIO 的特點:在內核進行 IO 執行的兩個階段,用戶線程都被 block 了。

BIO 的優點:程序簡單,在阻塞等待數據期間,用戶線程掛起,用戶線程基本不會佔用 CPU 資源。

BIO 的缺點:一般情況下,會爲每個連接配套一條獨立的線程,或者說一條線程維護一個連接成功的 IO 流的讀寫。在併發量小的情況下,這個沒有什麼問題。但是,當在高併發的場景下,需要大量的線程來維護大量的網絡連接,內存、線程切換開銷會非常巨大。因此,基本上,BIO 模型在高併發場景下是不可用的。

二、同步非阻塞 IO(NIO)

當用戶線程調用了 read 系統調用,立即返回,不阻塞線程,用戶線程需要不斷地發起 IO 系統調用輪詢數據是否準備好;

kernel 數據準備好後,用戶線程發起系統調用,用戶線程阻塞。內核開始複製數據,它就會將數據從 kernel 內核緩衝區,拷貝到用戶緩衝區(用戶內存),然後 kernel 返回結果。

用戶線程解除 block 狀態,重新運作起來。

NIO 的特點:應用程序的線程需要不斷的進行 I/O 系統調用,輪詢數據是否已經準備好,如果沒有準備好,繼續輪詢,直到完成系統調用爲止。

NIO 的優點:每次發起的 IO 系統調用,在內核的等待數據過程中可以立即返回,用戶線程不會阻塞,實時性較好。

NIO 的缺點:需要不斷的重複發起 IO 系統調用,這種不斷的輪詢,將會不斷地詢問內核,這將佔用大量的 CPU 時間,系統資源利用率較低。

NIO 模型在高併發場景下,也是不可用的。一般 web 服務器不直接使用這種 IO 模型,而是在其他 IO 模型中使用非阻塞 IO 這一特性。java 的實際開發中,也不會涉及這種 IO 模型。

三、IO 多路複用

當用戶線程調用了 read 系統調用,用戶線程不直接訪問 kernel ,而是進行 select/poll/epoll(多路複用器)系統調用。當然,這裏有一個前提,需要將目標網絡連接,提前註冊到 select/poll/epoll 的可查詢 socket 列表中(這部分由 kernel 完成)。

用戶線程進行 select/poll/epoll 系統調用,線程阻塞,kernel 會查詢所有 select/poll/epoll 的可查詢 socket 列表,當任何一個 socket 中的數據準備好了,select/poll/epoll 就會返回。

用戶線程獲得了目標連接後,發起 read 系統調用,線程阻塞,內核開始複製數據,它就會將數據從 kernel 內核緩衝區,拷貝到用戶緩衝區(用戶內存),然後 kernel 返回結果。

用戶線程才解除 block 的狀態,用戶線程終於真正讀取到數據,繼續執行。

多路複用 IO 的特點:

  1. 建立在操作系統 kernel 內核能夠提供的多路複用系統調用 select/poll/epoll 基礎之上的,多路複用 IO 需要用到兩個系統調用(system call), 一個 select/poll/epoll 查詢調用,一個是 IO 的讀取調用。
  2. 和 NIO 模型類似,多路複用 IO 需要輪詢,需要有單獨的線程不斷的進行 select/poll/epoll 輪詢,查找出可以進行 IO 操作的連接。
  3. 多路複用 IO 模型與前面的 NIO 模型是有關係的,對於每一個可以查詢的 socket,一般都設置成爲 non-blocking 模型。

多路複用 IO 的優點:用 select/poll/epoll 的優勢在於,它可以同時處理成千上萬個連接(connection)。與一條線程維護一個連接相比,I/O 多路複用不必創建線程,也不必維護這些線程,從而大大減小了系統的開銷。

多路複用 IO 的缺點:本質上,select/poll/epoll 系統調用,屬於同步 IO,也是阻塞 IO,需要在讀寫事件就緒後,自己負責進行讀寫,也就是說這個讀寫過程是阻塞的。

tips:

  1. "多路"指的是多個連接;"複用"指的是複用一個進程/線程進行監控。
  2. Java 的 NIO(New IO)技術,使用的就是 IO 多路複用模型。在 linux 系統上,使用的是 epoll 系統調用。

四、異步非阻塞IO(AIO)

當用戶線程調用了 read 系統調用,用戶線程立刻就能去做其它的事,用戶線程不阻塞。

內核(kernel)就開始了 IO 的第一個階段:準備數據,當 kernel 一直等到數據準備好了,它就會將數據從 kernel 內核緩衝區,拷貝到用戶緩衝區(用戶內存)。

然後,kernel 會給用戶線程發送一個信號(signal),或者回調用戶線程註冊的回調接口,告訴用戶線程 read 操作完成了。

用戶線程讀取用戶緩衝區的數據,完成後續的業務操作。

AIO 的特點:

  1. 在內核 kernel 的等待數據和複製數據的兩個階段,用戶線程都不是 block 的。
  2. 用戶線程需要接受 kernel 的 IO 操作完成的事件,或者說註冊 IO 操作完成的回調函數到操作系統的內核,因此,異步 IO 有的時候也叫做信號驅動 IO。

AIO 的缺點:需要完成事件的註冊與傳遞,這裏邊需要底層操作系統提供大量的支持,去做大量的工作。

目前來說, Windows 系統下通過 IOCP 實現了真正的異步 I/O,但是,就目前的業界形式而言,Windows 系統,很少作爲百萬級以上或者說高併發應用的服務器操作系統來使用。

而在 Linux 系統下,異步 IO 模型在2.6版本才引入,目前並不完善。所以,這也是在 Linux 下,實現高併發網絡編程時都是以 IO 複用模型模式爲主。(https://github.com/netty/netty/issues/2515

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