搜尋
首頁運維linux運維談談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呼叫:<pre class="brush:php;toolbar:false">buf = mmap(diskfd, len); write(sockfd, buf, len);</pre>應用程式呼叫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刪除
Linux中的維護模式:何時以及為什麼使用它Linux中的維護模式:何時以及為什麼使用它Apr 25, 2025 am 12:15 AM

使用Linux維護模式的時機和原因:1)系統啟動問題時,2)進行重大系統更新或升級時,3)執行文件系統維護時。維護模式提供安全、控制的環境,確保操作的安全性和效率,減少對用戶的影響,並增強系統的安全性。

Linux:基本命令和操作Linux:基本命令和操作Apr 24, 2025 am 12:20 AM

Linux中不可或缺的命令包括:1.ls:列出目錄內容;2.cd:改變工作目錄;3.mkdir:創建新目錄;4.rm:刪除文件或目錄;5.cp:複製文件或目錄;6.mv:移動或重命名文件或目錄。這些命令通過與內核交互執行操作,幫助用戶高效管理文件和系統。

Linux操作:管理文件,目錄和權限Linux操作:管理文件,目錄和權限Apr 23, 2025 am 12:19 AM

在Linux中,文件和目錄管理使用ls、cd、mkdir、rm、cp、mv命令,權限管理使用chmod、chown、chgrp命令。 1.文件和目錄管理命令如ls-l列出詳細信息,mkdir-p遞歸創建目錄。 2.權限管理命令如chmod755file設置文件權限,chownuserfile改變文件所有者,chgrpgroupfile改變文件所屬組。這些命令基於文件系統結構和用戶、組系統,通過系統調用和元數據實現操作和控制。

Linux中的維護模式是什麼?解釋了Linux中的維護模式是什麼?解釋了Apr 22, 2025 am 12:06 AM

MaintenancemodeInuxisAspecialBootenvironmentforforcalsystemmaintenancetasks.itallowsadMinistratorStoperFormTaskSlikerSettingPassingPassingPasswords,RepairingFilesystems,andRecoveringFrombootFailuresFailuresFailuresInamInimAlenimalenimalenrenmentrent.ToEnterMainterMainterMaintErmaintErmaintEncemememodeBoode,Interlecttheboo

Linux:深入研究其基本部分Linux:深入研究其基本部分Apr 21, 2025 am 12:03 AM

Linux的核心組件包括內核、文件系統、Shell、用戶空間與內核空間、設備驅動程序以及性能優化和最佳實踐。 1)內核是系統的核心,管理硬件、內存和進程。 2)文件系統組織數據,支持多種類型如ext4、Btrfs和XFS。 3)Shell是用戶與系統交互的命令中心,支持腳本編寫。 4)用戶空間與內核空間分離,確保系統穩定性。 5)設備驅動程序連接硬件與操作系統。 6)性能優化包括調整系統配置和遵循最佳實踐。

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

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

Linux操作:利用維護模式Linux操作:利用維護模式Apr 19, 2025 am 12:08 AM

Linux的維護模式可以通過GRUB菜單進入,具體步驟為:1)在GRUB菜單中選擇內核並按'e'編輯,2)在'linux'行末添加'single'或'1',3)按Ctrl X啟動。維護模式提供了一個安全環境,適用於系統修復、重置密碼和系統升級等任務。

Linux:如何進入恢復模式(和維護)Linux:如何進入恢復模式(和維護)Apr 18, 2025 am 12:05 AM

進入Linux恢復模式的步驟是:1.重啟系統並按特定鍵進入GRUB菜單;2.選擇帶有(recoverymode)的選項;3.在恢復模式菜單中選擇操作,如fsck或root。恢復模式允許你以單用戶模式啟動系統,進行文件系統檢查和修復、編輯配置文件等操作,幫助解決系統問題。

See all articles

熱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

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

熱工具

Safe Exam Browser

Safe Exam Browser

Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )專業的PHP整合開發工具

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

MantisBT

MantisBT

Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

VSCode Windows 64位元 下載

VSCode Windows 64位元 下載

微軟推出的免費、功能強大的一款IDE編輯器