Stream、Channel

1、Stream 不會自動緩衝數據,Channel 利用系統提供的發送緩衝區、接收緩衝區(更底層)

2、Stream 僅支持阻塞 API,Channel 同時支持阻塞、非阻塞 API,網絡 Channel 可配合 Selector 實現多路複用

3、二者均為全雙工,即讀、寫可以同時進行,Stream 是單向流動,但它也是全雙工

 

IO 模型

1、當調用一次 Channel.read 或 Stream.read 後,會由用户態切換至操作系統內核態,完成真正數據讀取

2、讀取分為兩個階段

(1)等待數據階段

(2)複製數據階段

什麼是雙流RNN架構_用户態

 

阻塞 IO

1、用户線程進行 read 操作時,需要等待操作系統執行實際 read 操作,期間用户線程被阻塞,無法執行其他操作

什麼是雙流RNN架構_數據_02

什麼是雙流RNN架構_內核態_03

 

非阻塞 IO

1、用户線程在一個循環中,一直調用 read 方法,若內核空間中還沒有數據可讀,立即返回

2、只在等待階段非阻塞

3、用户線程發現內核空間中有數據後,等待內核空間執行復制數據,待複製結束後返回結果

什麼是雙流RNN架構_什麼是雙流RNN架構_04

什麼是雙流RNN架構_內核態_05

 

多路複用

1、Java 通過 Selector 實現多路複用

(1)當沒有事件時,調用 select 方法會被阻塞

(2)一旦有一個或多個事件發生後,就會處理對應的事件,從而實現多路複用

什麼是雙流RNN架構_什麼是雙流RNN架構_06

什麼是雙流RNN架構_用户態_07

2、區分

(1)多線程 + 阻塞 IO:每個 Socket 對應一個線程,佔用較多資源

(2)多路複用:一個線程就可以管理多個 Socket,只有當 Socket 真正有事件發生,才會佔用資源進行實際操作

(3)非阻塞 IO:通過用户線程不斷詢問 Socket 狀態

(4)多路複用 IO:在內核中輪詢每個 Socket 狀態

3、事項

(1)多路複用通過輪詢方式檢測是否有事件發生,並且對到達的事件逐一進行響應,一旦事件響應體很大,那麼就會導致後續的事件遲遲得不到處理,並且會影響新的事件輪詢

(2)多路複用並不是非阻塞,只是它可以同時處理多個連接,單路是一個處理完才能接入新的連接,多路複用就是可以同時接入多個連接,只要新連接準備好就可以返回,然後會記錄所有連接的信息,處理完返回響應

(3)多路複用是使用一個線程來檢查多個文件描述符(Socket)就緒狀態,傳入多個文件描述符,如果有一個文件描述符就緒,則返回,否則阻塞直到超時,得到就緒狀態後,操作可以在同一個線程裏執行,也可以啓動線程執行(如:線程池)

 

信號驅動

1、當進程發起一個IO操作,會向內核註冊一個信號處理函數,然後進程返回不阻塞;當內核數據就緒時會發送一個信號給進程,進程便在信號處理函數中調用IO讀取數據

2、信號類似於硬件上使用的中斷,只不過信號是軟件層面上的,可理解為軟件層次上對中斷的一種模擬,驅動通過主動向應用程序發送可訪問的信號,應用程序獲取到信號後,即可從驅動設備中讀取或寫入數據

什麼是雙流RNN架構_內核態_08

 

異步 IO

1、線程 1 調用方法後立即返回,不會被阻塞,也不需要立即獲取結果

2、當方法的運行結果出後,由線程 2 將結果返回給線程 1

什麼是雙流RNN架構_數據_09

什麼是雙流RNN架構_用户態_10

 

五種 IO 模型

什麼是雙流RNN架構_什麼是雙流RNN架構_11

 

零拷貝

1、數據無需拷貝到 JVM 內存中

2、優點

(1)更少的用户態與內核態的切換

(2)不利用 CPU 計算,減少 CPU 緩存偽共享

(3)適合小文件傳輸

 

傳統 IO 內部工作流

什麼是雙流RNN架構_用户態_12

1、Java 本身並不具備 IO 讀寫能力,需要從 Java 程序的用户態切換至內核態

2、調用 read 方法

(1)調用操作系統(Kernel)讀,用户態切換至內核態,將數據讀入內核緩衝區

(2)期間用户線程阻塞

(3)操作系統使用 DMA(Direct Memory Access)實現文件讀,期間也不會使用 CPU

(4)DMA 可以理解為硬件單元,用來解放 CPU 完成文件 IO

(5)從內核態切換回用户態,將數據從內核緩衝區,讀入用户緩衝區,期間 CPU 參與拷貝,無法利用 DMA

3、調用 write 方法

(1)將數據從用户緩衝區,寫入 Socket 緩衝區,CPU 參與拷貝

(2)向網卡寫數據,從用户態切換至內核態,調用操作系統的寫,使用 DMA 將 Socket 緩衝區的數據寫入網卡,不會使用 CPU

4、問題

(1)Java 的 IO 實際不是物理設備級別的讀寫,而是緩存的複製,底層的真正讀寫是操作系統來完成的

(2)用户態與內核態的切換髮生了 3 次,這個操作比較重量級

(3)數據共拷貝 4 次

 

NIO 優化

1、通過 DirectByteBuf

(1)ByteBuffer.allocate:底層對應 HeapByteBuffer,使用的還是 Java 內存

(2)ByteBuffer.allocateDirect:底層對應 DirectByteBuffer,使用的是操作系統內存

什麼是雙流RNN架構_內核態_13

2、Java 可以使用 DirectByteBuffer,將堆外內存映射到 JVM 內存中來直接訪問使用

(1)堆外內存不受 JVM 垃圾回收的影響,因此內存地址固定,有助於 IO 讀寫

(2)Java 中的 DirectByteBuf 對象僅維護此內存的虛引用

3、內存回收

(1)DirectByteBuffer 對象被垃圾回收,將虛引用加入引用隊列

(2)當引用的對象 ByteBuffer 被垃圾回收以後,虛引用對象 Cleaner 就會被放入引用隊列中,然後調用 Cleaner 的 clean 方法來釋放直接內存

(3)DirectByteBuffer 釋放底層,調用 Unsafe 的 freeMemory 方法

(4)通過專門線程訪問引用隊列,根據虛引用釋放堆外內存

4、減少一次數據拷貝,用户態與內核態的切換次數沒有減少

 

零拷貝優化 1

1、底層:Linux 2.1 提供的 sendFile 方法

什麼是雙流RNN架構_什麼是雙流RNN架構_14

2、Java 中對應着兩個 Channel 調用 transferTo / transferFrom 方法拷貝數據

(1)Java 調用 transferTo 方法後,要從 Java 程序的用户態切換至內核態,使用 DMA 將數據讀入內核緩衝區,不會使用 CPU

(2)數據從內核緩衝區傳輸到 Socket 緩衝區,CPU 參與拷貝

(2)最後使用 DMA,將 Socket 緩衝區的數據寫入網卡,不會使用 CPU

3、性能

(1)只發生 1 次用户態與內核態的切換

(2)數據拷貝 3 次

 

零拷貝優化 2

1、Linux 2.4 對 sendFile 優化

什麼是雙流RNN架構_數據_15

2、過程

(1)Java 調用 transferTo 方法後,從 Java 程序的用户態切換至內核態,使用 DMA 將數據讀入內核緩衝區,不會使用 CPU

(2)只會將一些 offset 和 length 信息拷入 Socket 緩衝區,幾乎無消耗

(3)使用 DMA 將 內核緩衝區的數據寫入網卡,不會使用 CPU

3、性能

(1)只發生 1 次用户態與內核態的切換

(2)數據拷貝 2 次

 

AIO

1、異步非阻塞

2、解決數據複製階段的阻塞問題

(1)同步:在進行讀寫操作時,線程需要等待結果,相當於閒置

(2)異步:在進行讀寫操作時,線程不必等待結果,而是將來由操作系統來通過回調方式,由另外的線程來獲得結果

3、異步模型需要底層操作系統(Kernel)提供支持

(1)Windows 系統通過 IOCP 實現真正的異步 IO

(2)Linux 系統異步 IO 在 2.6 版本引入,但底層實現還是使用多路複用模擬異步 IO,性能沒有優勢