零拷貝技術深入分析

一、零拷貝

在前面的文章“深淺拷貝、COW及零拷貝”中對零拷貝進行過分析,但沒有舉例子,也沒有深入進行展開分析。本文將結合實際的例程對零拷貝進行更深入的分析和說明。
在傳統的IO操作中,以文件通過網絡傳輸為例 ,一般會經歷以下幾個數據拷貝的過程:
磁盤緩沖區 ->內核緩沖區->用戶緩沖區->內核網絡緩沖區->網卡緩沖區
也就是數據要經歷從IO到內核空間,再從內核到用戶空間再進入內核空間然后才能通過IO發走,至少要有四次的內在拷貝。
而這就引出了零拷貝的概念:盡最大可能減少CPU參與數據拷貝的過程(直到完全不參與拷貝)。它主要有基于內核緩沖優化的零拷貝和DirectIO的零拷貝。
仍然以上面的鏈路來分析,可不可以直接從硬盤把數據(內核緩沖區)拷貝到網卡緩沖區,可不可以?可不可以不過用戶緩沖區直接在內核內交互數據?這都是直接想到的解決問題的方法和手段。而實際上,零拷貝技術也就是按這種指導思想進行開展的。
零拷貝技術的實現有以下幾種方法:
1、DirectIO
這個好理解,不通過各種中間環節直接和IO打交道。它主要應用于上層應用本身實現了磁盤的數據緩存,比如常見的數據庫系統軟件,那么就不需要再使用PageCache進行緩沖。這樣就可以減少PageCache(內核緩沖區)的消耗(這可略過了計算中最大的中間商CPU)。而諸如下面的sendfile等,其實都基于PageCache優化的零拷貝。
2、新的函數sendfile(win:TransmitFile)
sendfile是Linux系統提供的系統API,它可以解決用戶空間和內核空間的數據拷貝的次數問題;如果其和DMA技術(重點指SG-DMA(The Scatter-Gather Direct Memory Access))共同工作即sendfile+DMA,那么其效率更高,可以直接把數據文件從磁盤拷貝到網絡緩沖區 。
sendfile有其一定的局限性,首先是標準不統一,另外一個就是無法在數據操作中間在用戶空間對數據進行操作,比如從磁盤加載然后加解密等然后再發送,因為得不到具體的數據 ,這需要引起重視。
3、函數splice
splice技術更進一步,它接近于 sendfile和DMA的進一步效率提高,此函數在內核空間和網絡緩沖區間建立管道,避免二者的CPU的拷貝。注意,此函數中的兩個文件操作符必須有一個為管道操作符。
4、mmap
mmap方式大家比較熟悉,這里就簡單說明一下,其實mmap的零拷貝就是通過內存映射提供一個內核和用戶空間直接通信的手段。mmap應用非常多,最典型的是安卓的應用,Framework層的數據通信很多是用mmap為實現的。
5、tee
tee函數用來在兩個管道文件描述符間復制數據。它要求兩個文件描述符都必須為管道描述符;同時,它在復制過程中保持原數據不動直接復制fd,而splice是移動數據從源fd到目的fd。注意二者的區別和不同。
下面就分別對幾類技術實現方式進行舉例分析。在分析之前,先對原來的文章“深淺拷貝、COW及零拷貝”中零拷貝的圖進行一下完善:

在這里插入圖片描述

主要是補齊了未描述清楚的普通DMA部分的流程。

二、sendfile

先看一下定義:

int main(int argc, char* argv[])
{
......int ffd = open(fname, O_RDONLY);//打開文件struct stat st;fstat(ffd, &st);struct sockaddr_in addr;bzero(&addr, sizeof(addr));addr.sin_family = AF_INET;inet_pton(AF_INET, ip, &addr.sin_addr);addr.sin_port = htons(static_cast<uint16_t>(port));int s = socket(PF_INET, SOCK_STREAM, 0);int reuse = 1;//設置端口重用setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(reuse));int ret = bind(s, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr));ret = listen(s, 3);struct sockaddr_in client;socklen_t client_addrlen = sizeof(client);int cSocket = accept(s, reinterpret_cast<struct sockaddr*>(&client), &client_addrlen);if (cSocket < 0) {printf("accept err: %d\n", errno);}else {sendfile(cSocket, ffd, NULL, static_cast<size_t>(st.st_size));close(cSocket);}......return 0;
}

注意上面的代碼省略了相關的安全控制和參數賦值,大家可以自行設置,直接寫成固定的就可以,只是一個測試程序么。

三、splice

splice的應用也不復雜,但需要注意其中的一些要求,特別是參數中,在Linux2.6.21以前,splice的flags設置SPLICE_F_MOVE有效,其后就無效了,但SPLICE_F_NONBLOCK 和SPLICE_F_MORE都有效果。看一下例程:

#include <fcntl.h>
#include <unistd.h>
#include <strings.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <libgen.h>
#include <assert.h>
#include <stdlib.h>int main(int argc, char* argv[])
{
......struct sockaddr_in addr;bzero(&addr, sizeof(addr));addr.sin_family = AF_INET;inet_pton(AF_INET, ip, &addr.sin_addr);addr.sin_port = htons(static_cast<uint16_t>(port));int sfd = socket(PF_INET, SOCK_STREAM, 0);int reuse = 1;setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(reuse));int r = bind(sockfd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr));r = listen(sockfd, 3);struct sockaddr_in cSocket;socklen_t client_addrlen = sizeof(cSocket);int cfd = accept(sfd, reinterpret_cast<sockaddr*>(&cSocket), &client_addrlen);if (cfd < 0) {printf("accept err: %d\n", errno);}else {int pfd[2];ret = pipe(pfd);while (1) {ssize_t res;res = splice(cfd, NULL, pfd[1], NULL, 1024, SPLICE_F_MORE | SPLICE_F_MOVE);if (res == 0) { // 收到EOFbreak;}res = splice(pfd[0], NULL, cfd, NULL, 1024, SPLICE_F_MORE | SPLICE_F_MOVE);}close(cfd);}close(sfd);return 0;
}

相關的具體參數可以看說明文檔,還是相當清楚的。

四、tee和mmap

mmap的例子非常多,這里只給一個tee相關的例子:


#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <libgen.h>
#include <assert.h>int main(int argc, char* argv[])
{
......int ffd = open(argv[1], O_CREAT | O_TRUNC | O_WRONLY, 0666);int pfdout[2];int r = pipe(pfdout);assert(r != -1);int pfdfile[2];r = pipe(pfdfile);while (1) {ssize_t res = splice(STDIN_FILENO, NULL, pfdout[1], NULL, 1024, SPLICE_F_MORE | SPLICE_F_MOVE);if (res == 0) {break;}res = tee(pfdout[0], pfdfile[1], 1024, SPLICE_F_NONBLOCK);res = splice(pfdfile[0], NULL, ffd, NULL, 1024, SPLICE_F_MORE | SPLICE_F_MOVE);assert(res != -1);// 二次調用,因為第一次調用數據已經移動,所以splice函數阻塞//res = splice(pfdfile[0], NULL, STDOUT_FILENO, NULL, 1024, SPLICE_F_MORE | SPLICE_F_MOVE);}.......return 0;
}

這些都沒有什么難度,手冊上也都有相關的例程。

五、DMA技術和零拷貝

在上面的分析過程中可以清晰的知道,DMA技術和零拷貝既有千絲萬縷的聯系,又有所不同:
DMA技術是負責數據的直通,零拷貝重點是CPU不參與數據拷貝,但需要參與數據的管理(比如數據可以使用,開始操作等等),也就是說DMA技術和零拷貝技術中的CPU互相協作,達到數據拷貝的次數最少的目的。
零拷貝其實就是考慮減少從IO到用戶層的整個數據流程的拷貝次數從而提高效率,要始終抓住這條主線。DMA主要是拷貝,CPU重點是管理,即把CPU從既管理又復制中簡化工作任務,只管理即可。DMA技術和硬件關系很密切,所以在具體的開發使用中,要明確硬件是否支持相關具體的操作。
需要注意的另外一點是,在實際場景中,如果是非常大的數據文件處理,基于PageCache零拷貝技術則有些力不從心了,還是得使用Direct IO的零拷貝技術。

六、使用零拷貝的框架

說一些技術和概念可能理解并不深刻,可以參考一下相關的一些開源框架中使用的零拷貝技術:
1、KAFKA
使用sendfile的零拷貝技術
2、Nginx
提供了sendfile和directio的相關零拷貝技術
3、Mysql
使用了directio的零拷貝技術
4、Netty
使用sendfile的零拷貝技術
5、RocketMQ
使用了mmap write的零拷貝技術

七、總結

其實說得更淺顯一些,所謂零拷貝更準確的說不是零次拷貝,是指盡可能的減少拷貝。在DPDK的系列文章中,這種操作被發揮的淋漓盡致。互聯網的口號就是“不讓中間商賺差價”,這個在現實上可能有一些邏輯上的BUG,但在內存操作上確實是非常用益。
當然,萬事萬物不是說是絕對的,有的時候,抽象一下,加一層,如果能達到更好的效果,又不影響實際的使用的情況下,豈不更妙?千頭萬緒又回到始終堅持的原則,應用場景決定應用技術,實踐是檢驗真理的標準。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/714757.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/714757.shtml
英文地址,請注明出處:http://en.pswp.cn/news/714757.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

go語言是如何連接mysql數據庫的?

在 Go 語言中連接 MySQL 數據庫通常使用第三方庫。目前比較流行的 MySQL 客戶端庫包括 github.com/go-sql-driver/mysql、github.com/go-xorm/xorm、github.com/jmoiron/sqlx 等。下面是一個使用 github.com/go-sql-driver/mysql 庫連接 MySQL 數據庫的簡單示例&#xff1a; 首…

GOPATH set to GOROOT (/usr/local/go) has no effect

這個警告信息指出 GOPATH 環境變量被設置為與 GOROOT 相同的值。GOPATH 環境變量用于指定 Go 工作區的位置&#xff0c;而 GOROOT 指定 Go 安裝的位置。將 GOPATH 設置為 GOROOT 表示 Go 工作區和 Go 安裝位置位于同一目錄&#xff0c;這是不推薦的。為了解決此問題&#xff0c…

Zookeeper集群docker部署

集群角色 zookeeper 集群中的機器分為一下三種角色 leader:為客戶端提供讀寫服務(事務性操作)&#xff0c;并維護集群狀態&#xff0c;它是由集群選舉所產生的&#xff1b; follower:為客戶端提供讀(非事務性操作)&#xff0c;轉發給leader寫(事務性操作)&#xff0c;參與選舉操…

K8s控制器

控制器: Deployment: Deployment概述: replicaset:自動創建pod的控制器 Delpoyment控制器: pod的名字需要唯一,在這不寫名字,利用標簽進行創建 replicas:表示你想要克隆的數量,selector:通過標簽.識別哪個pod是我創建出來的.這里的標簽和后面元數據里的標簽要一致. Cluster…

NCDA設計大賽獲獎作品剖析:UI設計如何脫穎而出?

第十二屆大賽簡介 - 未來設計師全國高校數字藝術設計大賽&#xff08;NCDA&#xff09;開始啦&#xff01;視覺傳達設計命題之一: ui 設計&#xff0c;你想知道的都在這里。為了讓大家更好的參加這次比賽&#xff0c;本文特別為大家整理了以往NCDA大賽 UI 設計的優秀獲獎作品&a…

【探索AI】十四深度學習之第2周:深度神經網絡(三)-過擬合與正則化技術

過擬合與正則化技術 過擬合的概念 在機器學習和深度學習的領域中&#xff0c;過擬合&#xff08;Overfitting&#xff09;是一個常見且重要的問題。首先&#xff0c;我們來理解一下什么是過擬合。 過擬合是指模型在訓練數據上表現得過于優秀&#xff0c;以至于在訓練集上的錯…

2024年騰訊云優惠券_代金券_云服務器折扣券免費領取鏈接

騰訊云優惠代金券領取入口共三個渠道&#xff0c;騰訊云新用戶和老用戶均可領取8888元代金券&#xff0c;可用于云服務器等產品購買、續費和升級使用&#xff0c;阿騰云atengyun.com整理騰訊云優惠券&#xff08;代金券&#xff09;領取入口、代金券查詢、優惠券兌換碼使用方法…

【SpringBean】bean的作用域和bean的生命周期

目錄 前言 一 bean的作用域 1. singleton——唯一 bean 實例 2. prototype——每次請求都會創建一個新的 bean 實例 3. request——每一次HTTP請求都會產生一個新的bean&#xff0c;該bean僅在當前HTTP request內有效 4. session——每一次HTTP請求都會產生一個新的 bean&…

程序員的金三銀四求職寶典!

目錄 ?編輯 程序員的金三銀四求職寶典 一、為什么金三銀四是程序員求職的黃金時期&#xff1f; 二、如何準備金三銀四求職&#xff1f; 1. 完善簡歷 2. 增強技術能力 3. 提前考慮目標公司 4. 提前準備面試 三、程序員求職的常見面試題 1. 數據結構和算法 2. 數據庫 …

一次電腦感染Synaptics Pointing Device Driver病毒的經歷,分享下經驗

沒想到作為使用電腦多年的老司機也會電腦中病毒&#xff0c;周末玩電腦的時候突然電腦很卡&#xff0c;然后自動重啟&#xff0c;奇怪&#xff0c;之前沒出現這個情況。 重啟后電腦開機等了幾十秒&#xff0c;打開任務管理器查看開機進程&#xff0c;果然發現有個Synaptics Po…

LeetCode 刷題 [C++] 第121題.買賣股票的最佳時機

題目描述 給定一個數組 prices &#xff0c;它的第 i 個元素 prices[i] 表示一支給定股票第 i 天的價格。 你只能選擇 某一天 買入這只股票&#xff0c;并選擇在 未來的某一個不同的日子 賣出該股票。設計一個算法來計算你所能獲取的最大利潤。 返回你可以從這筆交易中獲取的…

php兒童服裝銷售管理系統計算機畢業設計項目包運行調試

php mysql兒童服裝銷售網 功能&#xff1a;前臺后臺 前臺&#xff1a; 1.服裝資訊 文章標題列表 詳情 2.服裝選購中心 分頁查看圖文列表 詳情 3.用戶注冊 登陸 退出 4.服裝加入收藏 5.加入購物車 6.對服裝進行評論 會員中心&#xff1a; 1.我的賬戶 查看 修改 2.我的收藏 查看 …

STM32 中斷流程介紹

STM32可以產生中斷的事件多種多樣&#xff0c;比如&#xff1a;定時器時間結束、串口接收到數據、某個GPIO檢測到電平變化等等等等。 1、STM32 gpio 中斷處理流程介紹 1、從引腳進入的高低電平首先由輸入驅動器處理&#xff0c;如下圖 2、經過輸入驅動器處理后的信號會進…

大數據數據平臺的數倉體系分類有哪些?

大數據平臺的數倉體系最初由數據庫發展而來&#xff0c;主要分為三類架構&#xff0c;分別是Shared-Nothing、Shared-Data、Shared-Everything。 Shared-Nothing&#xff08;也稱 MPP&#xff09;架構在 很長一段時間成為主流。 隨云原生能力增強&#xff0c;Snowflake 為代表…

c++基礎知識補充4

單獨使用詞匯 using std::cout; 隱式類型轉換型初始化&#xff1a;如A a1,,此時可以形象地理解為int i1;double ji;&#xff0c;此時1可以認為創建了一個值為1的臨時對象&#xff0c;然后對目標對象進行賦值&#xff0c;當對象為多參數時&#xff0c;使用&#xff08;1&#xf…

c1-周考2

c1-第二周 9月-技能1.一個島上有兩種神奇動物&#xff0c;其中神奇鳥類2個頭3只腳&#xff0c;神奇獸類3個頭8只腳。游客在濃霧中看到一群動物&#xff0c;共看到35個頭和110只腳&#xff0c;求可能的鳥類和獸類的只數2.構建一個長度為5的數組&#xff0c;并且實現下列要求3.構…

鬼屋游戲c++

c #include <iostream> #include <string> #include <vector> #include <cstdlib> // 用于隨機數生成 #include <ctime> // 用于隨機數種子using namespace std;// 定義房間結構體 struct Room {string description;bool hasKey;bool hasClue…

babylonjs入門-自由相機 FreeCamera

基于babylonjs封裝的一些功能和插件 &#xff0c;希望有更多的小伙伴一起玩babylonjs&#xff1b; 歡迎加群&#xff08;點擊群號傳送&#xff09;&#xff1a;464146715 官方文檔 中文文檔 案例傳送門 懶得打字 粘貼復制 一氣呵成 ?

計算機網絡——22TCP擁塞

TCP擁塞 TCP擁塞控制機制 端到端的擁塞控制機制 路由器不向主機有關擁塞的反饋信息 路由器的負擔較輕符合網絡核心簡單的TCP/IP架構原則 端系統根據自身得到的信息&#xff0c;判斷是否發生擁塞&#xff0c;從而采取動作 擁塞控制的幾個問題 如何檢測擁塞 輕微擁塞擁塞 控…

javascript在現實中的應用

JavaScript是一種非常強大的編程語言&#xff0c;在現實世界中有廣泛的應用。它最初被設計用于網頁交互&#xff0c;但隨著時間的發展&#xff0c;其用途已經大大擴展。以下是JavaScript在現實中的一些主要應用&#xff1a; 1. **網頁開發**: - **前端開發**: JavaScript是…