日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【Netty】mmap 和 sendFile 零拷贝原理

發布時間:2025/6/17 编程问答 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【Netty】mmap 和 sendFile 零拷贝原理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 一、 零拷貝 簡介
  • 二、 傳統 BIO 數據拷貝分析 ( 4拷貝 4切換 )
  • 三、 mmap 內存映射 ( 3拷貝 4切換 )
  • 四、 sendFile 函數 ( Linux 2.1 優化 ) ( 3拷貝2切換 )
  • 五、 sendFile 函數 ( Linux 2.4 優化 ) ( 2拷貝 2切換 )





一、 零拷貝 簡介



零拷貝作用 : 在網絡編程中 , 如果要進行性能優化 , 肯定要涉及到零拷貝 , 使用零拷貝能極大的提升數據傳輸性能 ;


零拷貝類型 : mmap ( 內存映射 ) 和 sendFile;


數據角度分析 : 在零拷貝機制中 , 整個數據在內存中只有一份數據 , 非零拷貝機制中 , 內核緩沖區 , 用戶緩沖區 , Socket 緩沖區 , 各有一份數據 ;


零拷貝指的是沒有 CPU 拷貝 , 都是 DMA ( 直接內存訪問 ) 拷貝 ;


零拷貝性能優勢 : 沒有復制數據帶來的內存開銷 , 沒有 CPU 拷貝 , 直接節省了大量 CPU 計算資源 ;





二、 傳統 BIO 數據拷貝分析 ( 4拷貝 4切換 )



傳統 BIO 數據拷貝代碼示例 :

package kim.hsl.nio.zerocopy;import java.io.FileInputStream; import java.io.IOException; import java.net.Inet4Address; import java.net.InetSocketAddress; import java.net.Socket;public class BIOClientDemo {public static void main(String[] args) {try {// 客戶端與服務器端連接過程忽略, 主要是分析數據拷貝過程Socket socket = new Socket();InetSocketAddress inetSocketAddress =new InetSocketAddress(Inet4Address.getLocalHost(), 8888);socket.connect(inetSocketAddress);// 分析下面過程中, 數據拷貝次數, 和用戶態與內核態的轉換次數// 1. 從文件中讀取數據FileInputStream fileInputStream = new FileInputStream("file.txt");byte[] buffer = new byte[1024];// 首先將硬盤中的文件, 進行 DMA 拷貝, 此處對應 read 方法, // 將文件數據從硬盤中拷貝到 內核緩沖區 ( 用戶態切換成內核態 )// 將內核緩沖區中的數據, 通過 CPU 拷貝 方式, 拷貝到 用戶緩沖區 ( 內核態切換成用戶態 )int readLen = fileInputStream.read(buffer);// 2. 寫出數據到服務器// 將用戶緩沖區中的數據, 再次通過 CPU 拷貝方式, 拷貝到 Socket 緩沖區 ( 用戶態切換成內核態 )// 再次使用 DMA 拷貝, 將 Socket 緩沖區中的數據拷貝到 協議棧 ( Protocol Engine ) 中socket.getOutputStream().write(buffer, 0, readLen);} catch (IOException e) {e.printStackTrace();}} }

分析上述代碼中數據拷貝次數 , 用戶態與內核態狀態切換 ;



1 . fileInputStream.read(buffer) 操作數據拷貝及狀態轉換分析 :


① 硬盤 ( 初始用戶態 ) -> 內核緩沖區 ( 內核態 ) : 首先將硬盤中的文件 , 進行 DMA[1]^{[1]}[1] 拷貝 , 此處對應 read 方法 , 將文件數據從硬盤中拷貝到 內核緩沖區 ; ( 用戶態切換成內核態 )


② 內核緩沖區 ( 內核態 ) -> 用戶緩沖區 ( 用戶態 ) : 將內核緩沖區中的數據 , 通過 CPU 拷貝 方式 , 拷貝到 用戶緩沖區 ; ( 內核態切換成用戶態 )



2 . socket.getOutputStream().write(buffer, 0, readLen) 操作數據拷貝及狀態轉換分析 :


① 用戶緩沖區 ( 用戶態 ) -> Socket 緩沖區 ( 內核態 ) : 將用戶緩沖區中的數據 , 再次通過 CPU 拷貝 方式 , 拷貝到 Socket 緩沖區 ; ( 用戶態切換成內核態 )


② Socket 緩沖區 ( 內核態 ) -> 協議棧 : 再次使用 DMA[1]^{[1]}[1] 拷貝 , 將 Socket 緩沖區中的數據拷貝到 協議棧 ( Protocol Engine ) 中 ;



3 . 總結 : 上述進行了 444 次拷貝 , 333 次用戶態與內核態之間的狀態切換 , 代價很高 ;


① 拷貝次數分析 : 開始時數據存儲在 硬盤文件 中 , 直接內存拷貝 ( Direct Memory Access )內核緩沖區 , CPU 拷貝用戶緩沖區 , CPU 拷貝Socket 緩沖區 , 直接內存拷貝 ( Direct Memory Access )協議棧 ;


硬盤文件 -> 內核緩沖區 ( 內核空間 ) -> 用戶緩沖區 ( 用戶空間 ) -> Socket 緩沖區 ( 內核空間 ) -> 協議棧


② 狀態改變分析 : 開始運行的是用戶應用程序 , 起始狀態肯定是用戶態 , 之后將硬盤文件數據拷貝到內核緩沖區后 , 轉為內核態 , 之后又拷貝到了用戶緩沖區 , 轉為用戶態 ; 數據寫出到 Socket 緩沖區 , 又轉為內核態 , 最后再切換成用戶態 , 執行后續應用程序代碼邏輯 ;


用戶態 -> 內核態 -> 用戶態 -> 內核態 -> 用戶態



[1][1][1] DMA 全稱 ( Direct Memory Access ) , 直接內存拷貝 , 該拷貝通過內存完成 , 不涉及 CPU 參與 ;





三、 mmap 內存映射 ( 3拷貝 4切換 )



將硬盤中的文件映射到 內核緩沖區 , 用戶空間中的應用程序也可以訪問該 內核緩沖區 中的數據 , 使用這種機制 , 原來的 444 次數據拷貝減少到了 333 次 ,


1 . mmap 數據拷貝過程 :


① 硬盤文件 -> 內核緩沖區 : 硬盤文件數據 , DMA 拷貝到 內核緩沖區 中 , 應用程序可以直接訪問該 內核緩沖區中的數據 ;

② 內核緩沖區 -> Socket 緩沖區 : 內核緩沖區 數據 , 通過 CPU 拷貝到 Socket 緩沖區 ;

③ Socket 緩沖區 -> 協議棧 : Socket 緩沖區 數據 , 通過 DMA 拷貝到 協議棧 ;


硬盤文件 -> 內核緩沖區 ( 內核空間 ) -> Socket 緩沖區 ( 內核空間 ) -> 協議棧


2 . mmap 狀態切換 : 其狀態切換還是 333 , 由初始狀態 用戶態 , 在拷貝數據到內核緩沖區時 , 切換成內核態 , 訪問該內核緩沖區數據時 , 又切換成用戶態 , 將數據拷貝到 Socket 緩沖區時 , 切換成內核態 , 最后再切換成用戶態 , 執行后續應用程序代碼邏輯 ;


用戶態 -> 內核態 -> 用戶態 -> 內核態 -> 用戶態





四、 sendFile 函數 ( Linux 2.1 優化 ) ( 3拷貝2切換 )



sendFile 是 Linux 提供的函數 , 其實現了由 內核緩沖區 直接將數據拷貝到 Socket 緩沖區 , 該操作直接在內核空間完成 , 不經過用戶空間 , 沒有用戶態參與 , 因此 減少了一次用戶態切換 ;


此次優化 , 由原來的 444 次拷貝 , 333 次狀態切換 , 變成 333 次拷貝 , 222 次狀態切換 ;


1 . sendFile 函數 數據拷貝分析 :


① 硬盤文件 -> 內核緩沖區 : 硬盤文件數據 , DMA 拷貝到 內核緩沖區 中 ;

② 內核緩沖區 -> Socket 緩沖區 : 內核緩沖區 數據 , 通過 CPU 拷貝到 Socket 緩沖區 ;

③ Socket 緩沖區 -> 協議棧 : Socket 緩沖區 數據 , 通過 DMA 拷貝到 協議棧 ;

硬盤文件 -> 內核緩沖區 ( 內核空間 ) -> Socket 緩沖區 ( 內核空間 ) -> 協議棧


2 . sendFile 函數 狀態切換分析 : 其狀態切換只有 222 , 由初始狀態 用戶態 , 在拷貝數據到內核緩沖區時 , 切換成內核態 , 在內核態直接將數據拷貝到 Socket 緩沖區時 , 還是處于內核狀態 , 之后拷貝到協議棧時 , 變成用戶狀態 ;

用戶態 -> 內核態 -> 用戶態





五、 sendFile 函數 ( Linux 2.4 優化 ) ( 2拷貝 2切換 )



sendFile 是 Linux 提供的函數 , 其在 Linux 2.4 版本中 , 直接將數據從 內核緩沖區 拷貝到 協議棧 中 ;


此次優化 , 由原來的 444 次拷貝 , 333 次狀態切換 , 變成 222 次拷貝 , 222 次狀態切換 ;


1 . sendFile 函數 數據拷貝分析 : 全稱 DMA 拷貝 , 沒有 CPU 拷貝 ;


① 硬盤文件 -> 內核緩沖區 : 硬盤文件數據 , DMA 拷貝到 內核緩沖區 中 ;

② 內核緩沖區 -> -> 協議棧 : 通過 DMA 拷貝 , 將 內核緩沖區 中的數據直接拷貝到 協議棧 ;

硬盤文件 -> 內核緩沖區 ( 內核空間 ) -> 協議棧


2 . sendFile 函數 狀態切換分析 : 其狀態切換只有 222 , 由初始狀態 用戶態 , 在拷貝數據到內核緩沖區時 , 切換成內核態 , 在內核態直接將數據拷貝到協議棧時 , 變成用戶狀態 ;

用戶態 -> 內核態 -> 用戶態


3 . 少量 CPU 拷貝 : 該機制還存在少量的 CPU 拷貝 , 其 對性能的消耗忽略不計 ; 這些 CPU 拷貝操作是從 內核緩沖區 中將數據的長度 ( Length ) , 偏移量 ( Offset ) 拷貝到 Socket 緩沖區 ;

總結

以上是生活随笔為你收集整理的【Netty】mmap 和 sendFile 零拷贝原理的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。