一、同步阻塞 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 的特點:
- 建立在作業系統 kernel 內核能夠提供的多路復用系統呼叫 select/poll/epoll 基礎之上的,多路復用 IO 需要用到兩個系統呼叫(system call), 一個 select/poll/epoll 查詢呼叫,一個是 IO 的讀取呼叫,
- 和 NIO 模型類似,多路復用 IO 需要輪詢,需要有單獨的執行緒不斷的進行 select/poll/epoll 輪詢,查找出可以進行 IO 操作的連接,
- 多路復用 IO 模型與前面的 NIO 模型是有關系的,對于每一個可以查詢的 socket,一般都設定成為 non-blocking 模型,
多路復用 IO 的優點:用 select/poll/epoll 的優勢在于,它可以同時處理成千上萬個連接(connection),與一條執行緒維護一個連接相比,I/O 多路復用不必創建執行緒,也不必維護這些執行緒,從而大大減小了系統的開銷,
多路復用 IO 的缺點:本質上,select/poll/epoll 系統呼叫,屬于同步 IO,也是阻塞 IO,需要在讀寫事件就緒后,自己負責進行讀寫,也就是說這個讀寫程序是阻塞的,
tips:
- "多路"指的是多個連接;"復用"指的是復用一個行程/執行緒進行監控,
- Java 的 NIO(New IO)技術,使用的就是 IO 多路復用模型,在 linux 系統上,使用的是 epoll 系統呼叫,
四、異步非阻塞IO(AIO)

當用戶執行緒呼叫了 read 系統呼叫,用戶執行緒立刻就能去做其它的事,用戶執行緒不阻塞,
內核(kernel)就開始了 IO 的第一個階段:準備資料,當 kernel 一直等到資料準備好了,它就會將資料從 kernel 內核緩沖區,拷貝到用戶緩沖區(用戶記憶體),
然后,kernel 會給用戶執行緒發送一個信號(signal),或者回呼叫戶執行緒注冊的回呼介面,告訴用戶執行緒 read 操作完成了,
用戶執行緒讀取用戶緩沖區的資料,完成后續的業務操作,
AIO 的特點:
- 在內核 kernel 的等待資料和復制資料的兩個階段,用戶執行緒都不是 block 的,
- 用戶執行緒需要接受 kernel 的 IO 操作完成的事件,或者說注冊 IO 操作完成的回呼函式到作業系統的內核,因此,異步 IO 有的時候也叫做信號驅動 IO,
AIO 的缺點:需要完成事件的注冊與傳遞,這里邊需要底層作業系統提供大量的支持,去做大量的作業,
目前來說, Windows 系統下通過 IOCP 實作了真正的異步 I/O,但是,就目前的業界形式而言,Windows 系統,很少作為百萬級以上或者說高并發應用的服務器作業系統來使用,
而在 Linux 系統下,異步 IO 模型在2.6版本才引入,目前并不完善,所以,這也是在 Linux 下,實作高并發網路編程時都是以 IO 復用模型模式為主,(https://github.com/netty/netty/issues/2515)
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/235862.html
標籤:Java
上一篇:精盡Spring MVC原始碼分析 - HandlerMapping 組件(三)之 AbstractHandlerMethodMapping
