首頁 運維 linux運維 談談Linux的幾種零拷貝技術和適用的場景

談談Linux的幾種零拷貝技術和適用的場景

Jul 27, 2020 pm 05:40 PM
linux

談談Linux的幾種零拷貝技術和適用的場景

本文探討Linux中主要的幾種零拷貝技術以及零拷貝技術適用的場景。為了迅速建立起零拷貝的概念,我們拿一個常用的場景來介紹:

#引文

#在寫一個服務端程式時(Web Server或檔案伺服器),檔案下載是一個基本功能。這時候服務端的任務是:將服務端主機磁碟中的檔案不做修改地從已連接的socket發出去,我們通常用下面的程式碼完成:

while((n = read(diskfd, buf, BUF_SIZE)) > 0)
    write(sockfd, buf , n);
登入後複製

基本操作就是循環的從磁碟讀入檔案內容到緩衝區,再將緩衝區的內容傳送到socket。但是由於Linux的I/O操作預設是緩衝I/O。這裡面主要使用的也就是readwrite兩個系統調用,我們並不知道作業系統在其中做了什麼。實際上在上述I/O操作中,發生了多次的資料拷貝。

當應用程式存取某塊資料時,作業系統首先會檢查,是不是最近訪問過此文件,文件內容是否緩存在內核緩衝區,如果是,作業系統則直接根據read 系統呼叫提供的buf位址,將核心緩衝區的內容拷貝到buf所指定的使用者空間緩衝區中去。如果不是,作業系統則先將磁碟上的資料拷貝的核心緩衝區,這一步目前主要依靠DMA來傳輸,然後再把核心緩衝區上的內容拷貝到使用者緩衝區中。
接下來,write系統呼叫再把使用者緩衝區的內容拷貝到網路堆疊相關的核心緩衝區中,最後socket再把核心緩衝區的內容傳送到網路卡上。
說了這麼多,不如看圖清楚:

談談Linux的幾種零拷貝技術和適用的場景談談Linux的幾種零拷貝技術和適用的場景


#從上圖可以看出,共產生了四次資料拷貝,即使使用了DMA來處理了與硬體的通訊,CPU仍然需要處理兩次資料拷貝,同時,在用戶態與核心態也發生了多次上下文切換,無疑也加重了CPU負擔。

在這個過程中,我們沒有對檔案內容做任何修改,那麼在核心空間和使用者空間來回拷貝資料無疑就是一種浪費,而零拷貝主要就是為了解決這種低效性。

什麼是零拷貝技術(zero-copy)?

##零拷貝主要的任務就是避免CPU將資料從一塊儲存拷貝到另外一塊存儲,主要是利用各種零拷貝技術,避免讓CPU做大量的資料拷貝任務,減少不必要的拷貝,或是讓別的元件來做這一類簡單的資料傳輸任務,讓CPU解脫出來專注於別的任務。這樣就可以讓系統資源的利用更有效。

我們繼續回到引文中的例子,我們要如何減少資料拷貝的次數呢?一個很明顯的著力點就是減少資料在內核空間和使用者空間來回拷貝,這也引入了零拷貝的一個類型:

讓資料傳輸不需要經過user space

使用mmap

我們減少拷貝次數的一種方法是呼叫mmap()來代替read呼叫:<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">buf = mmap(diskfd, len); write(sockfd, buf, len);</pre><div class="contentsignin">登入後複製</div></div>應用程式呼叫mmap(),磁碟上的資料會透過DMA被拷貝的核心緩衝區,接著作業系統會把這段核心緩衝區與應用程式共享,這樣就不需要把核心緩衝區的內容往用戶空間拷貝。應用程式再呼叫write(),作業系統直接將核心緩衝區的內容拷貝到socket緩衝區中,這一切都發生在內核態,最後,
socket

緩衝區再把資料發到網卡去。

同樣的,看圖很簡單:談談Linux的幾種零拷貝技術和適用的場景

使用mmap替代read很明顯減少了一次拷貝,當拷貝資料量很大時,無疑提升了效率。但是使用mmap是有代價的。當你使用mmap時,你可能會遇到一些隱藏的陷阱。例如,當你的程式map了一個文件,但是當這個文件被另一個進程截斷(truncate)時, write系統呼叫會因為存取非法位址而被SIGBUS訊號終止。 SIGBUS訊號預設會殺死你的行程並產生一個

coredump###,如果你的伺服器這樣被中止了,那會產生一筆損失。 ###

通常我们使用以下解决方案避免这种问题:

1、为SIGBUS信号建立信号处理程序
当遇到SIGBUS信号时,信号处理程序简单地返回,write系统调用在被中断之前会返回已经写入的字节数,并且errno会被设置成success,但是这是一种糟糕的处理办法,因为你并没有解决问题的实质核心。

2、使用文件租借锁
通常我们使用这种方法,在文件描述符上使用租借锁,我们为文件向内核申请一个租借锁,当其它进程想要截断这个文件时,内核会向我们发送一个实时的RT_SIGNAL_LEASE信号,告诉我们内核正在破坏你加持在文件上的读写锁。这样在程序访问非法内存并且被SIGBUS杀死之前,你的write系统调用会被中断。write会返回已经写入的字节数,并且置errno为success。

我们应该在mmap文件之前加锁,并且在操作完文件后解锁:

if(fcntl(diskfd, F_SETSIG, RT_SIGNAL_LEASE) == -1) {
    perror("kernel lease set signal");
    return -1;
}
/* l_type can be F_RDLCK F_WRLCK  加锁*/
/* l_type can be  F_UNLCK 解锁*/
if(fcntl(diskfd, F_SETLEASE, l_type)){
    perror("kernel lease set type");
    return -1;
}
登入後複製

使用sendfile#####

从2.1版内核开始,Linux引入了sendfile来简化操作:

#include<sys>
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);</sys>
登入後複製

系统调用sendfile()在代表输入文件的描述符in_fd和代表输出文件的描述符out_fd之间传送文件内容(字节)。描述符out_fd必须指向一个套接字,而in_fd指向的文件必须是可以mmap的。这些局限限制了sendfile的使用,使sendfile只能将数据从文件传递到套接字上,反之则不行。
使用sendfile不仅减少了数据拷贝的次数,还减少了上下文切换,数据传送始终只发生在kernel space

談談Linux的幾種零拷貝技術和適用的場景

在我们调用sendfile时,如果有其它进程截断了文件会发生什么呢?假设我们没有设置任何信号处理程序,sendfile调用仅仅返回它在被中断之前已经传输的字节数,errno会被置为success。如果我们在调用sendfile之前给文件加了锁,sendfile的行为仍然和之前相同,我们还会收到RT_SIGNAL_LEASE的信号。

目前为止,我们已经减少了数据拷贝的次数了,但是仍然存在一次拷贝,就是页缓存到socket缓存的拷贝。那么能不能把这个拷贝也省略呢?

借助于硬件上的帮助,我们是可以办到的。之前我们是把页缓存的数据拷贝到socket缓存中,实际上,我们仅仅需要把缓冲区描述符传到socket缓冲区,再把数据长度传过去,这样DMA控制器直接将页缓存中的数据打包发送到网络中就可以了。

总结一下,sendfile系统调用利用DMA引擎将文件内容拷贝到内核缓冲区去,然后将带有文件位置和长度信息的缓冲区描述符添加socket缓冲区去,这一步不会将内核中的数据拷贝到socket缓冲区中,DMA引擎会将内核缓冲区的数据拷贝到协议引擎中去,避免了最后一次拷贝。

談談Linux的幾種零拷貝技術和適用的場景

不过这一种收集拷贝功能是需要硬件以及驱动程序支持的。

使用splice#####

sendfile只适用于将数据从文件拷贝到套接字上,限定了它的使用范围。Linux在2.6.17版本引入splice系统调用,用于在两个文件描述符中移动数据:

#define _GNU_SOURCE         /* See feature_test_macros(7) */
#include <fcntl.h>
ssize_t splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags);</fcntl.h>
登入後複製

splice调用在两个文件描述符之间移动数据,而不需要数据在内核空间和用户空间来回拷贝。他从fd_in拷贝len长度的数据到fd_out,但是有一方必须是管道设备,这也是目前splice的一些局限性。flags参数有以下几种取值:

  • SPLICE_F_MOVE :嘗試去移動資料而不是拷貝資料。這只是對核心的一個小提示:如果核心不能從pipe移動資料或pipe的快取不是一個整頁,仍然需要拷貝資料。 Linux最初的實作有些問題,所以從2.6.21開始這個選項不起作用,後面的Linux版本應該會實作。
  • ** SPLICE_F_NONBLOCK** :splice 操作不會被封鎖。然而,如果檔案描述子沒有被設定為不可被阻塞方式的 I/O ,那麼呼叫 splice 有可能仍然被阻塞。
  • ** SPLICE_F_MORE**: 後面的splice呼叫會有更多的資料。

splice呼叫利用了Linux提出的管道緩衝區機制, 所以至少一個描述符要為管道。

以上幾種零拷貝技術都是減少資料在使用者空間和核心空間拷貝技術實現的,但是有些時候,資料必須在使用者空間和核心空間之間拷貝。這時候,我們只能針對資料在使用者空間和核心空間拷貝的時機上下功夫了。 Linux通常利用寫時複製(copy on write)來減少系統開銷,這個技術又時常稱為COW

由於篇幅原因,本文不詳細介紹寫時複製。大概描述下就是:如果多個程式同時存取同一塊數據,那麼每個程式都擁有指向這塊數據的指針,在每個程式看來,自己都是獨立擁有這塊數據的,只有當程式需要對資料內容進行修改時,才會把資料內容拷貝到程式自己的應用程式空間裡去,這時候,資料才變成該程式的私有資料。如果程式不需要對資料進行修改,那麼永遠都不需要拷貝資料到自己的應用空間。這樣就減少了資料的拷貝。寫時複製的內容可以再寫一篇文章了。 。 。

除此之外,還有一些零拷貝技術,例如傳統的Linux I/O中加上O_DIRECT標記可以直接I/O,避免了自動緩存,還有尚未成熟的fbufs技術,本文尚未涵蓋所有零拷貝技術,只是介紹常見的一些,如有興趣,可以自行研究,一般成熟的服務端項目也會自己改造內核中有關I/O的部分,提高自己的資料傳輸速率。

推薦教學:《Linux運維

以上是談談Linux的幾種零拷貝技術和適用的場景的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

Linux體系結構:揭示5個基本組件 Linux體系結構:揭示5個基本組件 Apr 20, 2025 am 12:04 AM

Linux系統的五個基本組件是:1.內核,2.系統庫,3.系統實用程序,4.圖形用戶界面,5.應用程序。內核管理硬件資源,系統庫提供預編譯函數,系統實用程序用於系統管理,GUI提供可視化交互,應用程序利用這些組件實現功能。

vscode需要什麼電腦配置 vscode需要什麼電腦配置 Apr 15, 2025 pm 09:48 PM

VS Code 系統要求:操作系統:Windows 10 及以上、macOS 10.12 及以上、Linux 發行版處理器:最低 1.6 GHz,推薦 2.0 GHz 及以上內存:最低 512 MB,推薦 4 GB 及以上存儲空間:最低 250 MB,推薦 1 GB 及以上其他要求:穩定網絡連接,Xorg/Wayland(Linux)

notepad怎麼運行java代碼 notepad怎麼運行java代碼 Apr 16, 2025 pm 07:39 PM

雖然 Notepad 無法直接運行 Java 代碼,但可以通過借助其他工具實現:使用命令行編譯器 (javac) 編譯代碼,生成字節碼文件 (filename.class)。使用 Java 解釋器 (java) 解釋字節碼,執行代碼並輸出結果。

vscode終端使用教程 vscode終端使用教程 Apr 15, 2025 pm 10:09 PM

vscode 內置終端是一個開發工具,允許在編輯器內運行命令和腳本,以簡化開發流程。如何使用 vscode 終端:通過快捷鍵 (Ctrl/Cmd ) 打開終端。輸入命令或運行腳本。使用熱鍵 (如 Ctrl L 清除終端)。更改工作目錄 (如 cd 命令)。高級功能包括調試模式、代碼片段自動補全和交互式命令歷史。

git怎麼查看倉庫地址 git怎麼查看倉庫地址 Apr 17, 2025 pm 01:54 PM

要查看 Git 倉庫地址,請執行以下步驟:1. 打開命令行並導航到倉庫目錄;2. 運行 "git remote -v" 命令;3. 查看輸出中的倉庫名稱及其相應的地址。

vscode在哪寫代碼 vscode在哪寫代碼 Apr 15, 2025 pm 09:54 PM

在 Visual Studio Code(VSCode)中編寫代碼簡單易行,只需安裝 VSCode、創建項目、選擇語言、創建文件、編寫代碼、保存並運行即可。 VSCode 的優點包括跨平台、免費開源、強大功能、擴展豐富,以及輕量快速。

Linux的主要目的是什麼? Linux的主要目的是什麼? Apr 16, 2025 am 12:19 AM

Linux的主要用途包括:1.服務器操作系統,2.嵌入式系統,3.桌面操作系統,4.開發和測試環境。 Linux在這些領域表現出色,提供了穩定性、安全性和高效的開發工具。

vscode終端命令不能用 vscode終端命令不能用 Apr 15, 2025 pm 10:03 PM

VS Code 終端命令無法使用的原因及解決辦法:未安裝必要的工具(Windows:WSL;macOS:Xcode 命令行工具)路徑配置錯誤(添加可執行文件到 PATH 環境變量中)權限問題(以管理員身份運行 VS Code)防火牆或代理限制(檢查設置,解除限制)終端設置不正確(啟用使用外部終端)VS Code 安裝損壞(重新安裝或更新)終端配置不兼容(嘗試不同的終端類型或命令)特定環境變量缺失(設置必要的環境變量)

See all articles