深入理解零拷貝技術


前言

零拷貝技術是指計算機執行操作時,CPU不需要先將數據從某處內存復制到另一個特定區域。這種技術通常用於通過網絡傳輸文件時節省CPU周期和內存帶寬。

原始的網絡請求,需要數次在用戶態和內核態之間切換以及數據的拷貝,這無疑大大影響了處理的效率,零拷貝技術就是為解決這一問題而誕生的。

我們常見的高性能組件(Netty、Kafka等),其內部基本都應用了零拷貝,在學習這些組件之前,有必要先了解什么是零拷貝。

傳統文件傳輸 read + write

DMA拷貝:指外部設備不通過CPU而直接與系統內存交換數據的接口技術

如上圖所示,傳統的網絡傳輸,需要進行4次用戶態和內核態切換,4次數據拷貝(2次CPU拷貝,2次DMA拷貝)

上下文的切換涉及到操作系統,相對CPU速度是非常耗時的,而且僅僅一次文件傳輸,竟然需要4次數據拷貝,造成CPU資源極大的浪費

不難看出,傳統網絡傳輸涉及很多冗余且無意義的操作,導致應用在高並發情況下,性能指數級下降,表現異常糟糕

為了解決這一問題,零拷貝技術誕生了,他其實是一個抽象的概念,但其本質就是通過減少上下文切換和數據拷貝次數來實現的

mmap + write

如上圖所示,mmap技術傳輸文件,需要進行4次用戶態和內核態切換,3次數據拷貝(1次CPU拷貝、兩次DMA拷貝)

相對於傳統數據傳輸,mmap減少了一次CPU拷貝,其具體過程如下:

  1. 應用進程調用 mmap() ,DMA 會把磁盤的數據拷貝到內核的緩沖區里,應用進程跟操作系統內核「共享」這個緩沖區
  2. 應用進程再調用 write(),操作系統直接將內核緩沖區的數據拷貝到 socket 緩沖區中,這一切都發生在內核態,由 CPU 來搬運數據
  3. 最后,把內核的 socket 緩沖區里的數據,拷貝到網卡的緩沖區里,這個過程是由 DMA 搬運的

顯然僅僅減少一次數據拷貝,依然難以滿足要求

sendfile

如上圖所屬,sendfile技術傳輸文件,需要進行2次用戶態和內核態的切換,3次數據拷貝(1次CPU拷貝、兩次DMA拷貝)

相對於mmap,其又減少了兩次上下文的切換,具體過程如下:

  1. 應用調用sendfile接口,傳入文件描述符,應用程序切換至內核態,並通過 DMA 將磁盤上的數據拷貝到內核緩沖區中
  2. CPU將緩沖區數據拷貝至Socket緩沖區
  3. DMA將數據拷貝到網卡的緩沖區里,應用程序切換至用戶態

sendfile其實是將原來的兩步讀寫操作進行了合並,從而減少了2次上下文的切換,但其仍然不是真正意義上的“零”拷貝

sendfile + SG-DMA

從 Linux 內核 2.4 版本開始起,對於支持網卡支持 SG-DMA 技術的情況下, sendfile() 系統調用的過程發生了點變化,如上圖所示,sendfile + SG-DMA技術傳輸文件,需要進行2次用戶態和內核態的切換,2次數據拷貝(1次DMA拷貝,1次SG-DMA拷貝)

具體過程如下:

  1. 通過 DMA 將磁盤上的數據拷貝到內核緩沖區里;
  2. 緩沖區描述符和數據長度傳到 socket 緩沖區,這樣網卡的 SG-DMA 控制器就可以直接將內核緩存中的數據拷貝到網卡的緩沖區里,此過程不需要將數據從操作系統內核緩沖區拷貝到 socket 緩沖區中,這樣就減少了一次數據拷貝;

此種方式對比之前的,真正意義上去除了CPU拷貝,CPU 的高速緩存再不會被污染了,CPU 可以去執行其他的業務計算任務,同時和 DMA 的 I/O 任務並行,極大地提升系統性能。

但他的劣勢也很明顯,強依賴於硬件的支持

splice

Linux 在 2.6.17 版本引入 splice 系統調用,不再需要硬件支持,同時還實現了兩個文件描述符之間的數據零拷貝。

splice 系統調用可以在內核空間的讀緩沖區(read buffer)和網絡緩沖區(socket buffer)之間建立管道(pipeline),從而避免了用戶緩沖區和Socket緩沖區的 CPU 拷貝操作。

基於 splice 系統調用的零拷貝方式,整個拷貝過程會發生 2次用戶態和內核態的切換,2次數據拷貝(2次DMA拷貝),具體過程如下:

  1. 用戶進程通過 splice() 函數向內核(kernel)發起系統調用,上下文從用戶態(user space)切換為內核態(kernel space)。
  2. CPU 利用 DMA 控制器將數據從主存或硬盤拷貝到內核空間(kernel space)的讀緩沖區(read buffer)。
  3. CPU 在內核空間的讀緩沖區(read buffer)和網絡緩沖區(socket buffer)之間建立管道(pipeline)。
  4. CPU 利用 DMA 控制器將數據從網絡緩沖區(socket buffer)拷貝到網卡進行數據傳輸。
  5. 上下文從內核態(kernel space)切換回用戶態(user space),splice 系統調用執行返回。

splice 拷貝方式也同樣存在用戶程序不能對數據進行修改的問題。除此之外,它使用了 Linux 的管道緩沖機制,可以用於任意兩個文件描述符中傳輸數據,但是它的兩個文件描述符參數中有一個必須是管道設備

總結

本文簡單介紹了 Linux 中的幾種 Zero-copy 技術,隨着技術的不斷發展,又出現了諸如:寫時復制、共享緩沖等技術,本文就不再贅述。

廣義的來講,Linux 的 Zero-copy 技術可以歸納成以下三大類:

  • 減少甚至避免用戶空間和內核空間之間的數據拷貝:在一些場景下,用戶進程在數據傳輸過程中並不需要對數據進行訪問和處理,那么數據在 Linux 的 Page Cache 和用戶進程的緩沖區之間的傳輸就完全可以避免,讓數據拷貝完全在內核里進行,甚至可以通過更巧妙的方式避免在內核里的數據拷貝。這一類實現一般是是通過增加新的系統調用來完成的,比如 Linux 中的 mmap(),sendfile() 以及 splice() 等。
  • 繞過內核的直接 I/O:允許在用戶態進程繞過內核直接和硬件進行數據傳輸,內核在傳輸過程中只負責一些管理和輔助的工作。這種方式其實和第一種有點類似,也是試圖避免用戶空間和內核空間之間的數據傳輸,只是第一種方式是把數據傳輸過程放在內核態完成,而這種方式則是直接繞過內核和硬件通信,效果類似但原理完全不同。
  • 內核緩沖區和用戶緩沖區之間的傳輸優化:這種方式側重於在用戶進程的緩沖區和操作系統的頁緩存之間的 CPU 拷貝的優化。這種方法延續了以往那種傳統的通信方式,但更靈活。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM