阿里面試官:知道零拷貝技術嗎?那你來講講
前言
零拷貝技術是指計算機執(zhí)行操作時,CPU不需要先將數(shù)據(jù)從某處內存復制到另一個特定區(qū)域。這種技術通常用于通過網絡傳輸文件時節(jié)省CPU周期和內存帶寬。
原始的網絡請求,需要數(shù)次在用戶態(tài)和內核態(tài)之間切換以及數(shù)據(jù)的拷貝,這無疑大大影響了處理的效率,零拷貝技術就是為解決這一問題而誕生的。
我們常見的高性能組件(Netty、Kafka等),其內部基本都應用了零拷貝,在學習這些組件之前,有必要先了解什么是零拷貝。
傳統(tǒng)文件傳輸 read + write

DMA拷貝:指外部設備不通過CPU而直接與系統(tǒng)內存交換數(shù)據(jù)的接口技術
如上圖所示,傳統(tǒng)的網絡傳輸,需要進行4次用戶態(tài)和內核態(tài)切換,4次數(shù)據(jù)拷貝(2次CPU拷貝,2次DMA拷貝)
上下文的切換涉及到操作系統(tǒng),相對CPU速度是非常耗時的,而且僅僅一次文件傳輸,竟然需要4次數(shù)據(jù)拷貝,造成CPU資源極大的浪費
不難看出,傳統(tǒng)網絡傳輸涉及很多冗余且無意義的操作,導致應用在高并發(fā)情況下,性能指數(shù)級下降,表現(xiàn)異常糟糕
為了解決這一問題,零拷貝技術誕生了,他其實是一個抽象的概念,但其本質就是通過減少上下文切換和數(shù)據(jù)拷貝次數(shù)來實現(xiàn)的
mmap + write

如上圖所示,mmap技術傳輸文件,需要進行4次用戶態(tài)和內核態(tài)切換,3次數(shù)據(jù)拷貝(1次CPU拷貝、兩次DMA拷貝)
相對于傳統(tǒng)數(shù)據(jù)傳輸,mmap減少了一次CPU拷貝,其具體過程如下:
應用進程調用 mmap() ,DMA 會把磁盤的數(shù)據(jù)拷貝到內核的緩沖區(qū)里,應用進程跟操作系統(tǒng)內核「共享」這個緩沖區(qū)
應用進程再調用 write(),操作系統(tǒng)直接將內核緩沖區(qū)的數(shù)據(jù)拷貝到 socket 緩沖區(qū)中,這一切都發(fā)生在內核態(tài),由 CPU 來搬運數(shù)據(jù)
最后,把內核的 socket 緩沖區(qū)里的數(shù)據(jù),拷貝到網卡的緩沖區(qū)里,這個過程是由 DMA 搬運的
顯然僅僅減少一次數(shù)據(jù)拷貝,依然難以滿足要求
sendfile

如上圖所屬,sendfile技術傳輸文件,需要進行2次用戶態(tài)和內核態(tài)的切換,3次數(shù)據(jù)拷貝(1次CPU拷貝、兩次DMA拷貝)
相對于mmap,其又減少了兩次上下文的切換,具體過程如下:
應用調用sendfile接口,傳入文件描述符,應用程序切換至內核態(tài),并通過 DMA 將磁盤上的數(shù)據(jù)拷貝到內核緩沖區(qū)中
CPU將緩沖區(qū)數(shù)據(jù)拷貝至Socket緩沖區(qū)
DMA將數(shù)據(jù)拷貝到網卡的緩沖區(qū)里,應用程序切換至用戶態(tài)
sendfile其實是將原來的兩步讀寫操作進行了合并,從而減少了2次上下文的切換,但其仍然不是真正意義上的“零”拷貝
sendfile + SG-DMA

從 Linux 內核 2.4 版本開始起,對于支持網卡支持 SG-DMA 技術的情況下, sendfile() 系統(tǒng)調用的過程發(fā)生了點變化,如上圖所示,sendfile + SG-DMA技術傳輸文件,需要進行2次用戶態(tài)和內核態(tài)的切換,2次數(shù)據(jù)拷貝(1次DMA拷貝,1次SG-DMA拷貝)
具體過程如下:
通過 DMA 將磁盤上的數(shù)據(jù)拷貝到內核緩沖區(qū)里;
緩沖區(qū)描述符和數(shù)據(jù)長度傳到 socket 緩沖區(qū),這樣網卡的 SG-DMA 控制器就可以直接將內核緩存中的數(shù)據(jù)拷貝到網卡的緩沖區(qū)里,此過程不需要將數(shù)據(jù)從操作系統(tǒng)內核緩沖區(qū)拷貝到 socket 緩沖區(qū)中,這樣就減少了一次數(shù)據(jù)拷貝;
此種方式對比之前的,真正意義上去除了CPU拷貝,CPU 的高速緩存再不會被污染了,CPU 可以去執(zhí)行其他的業(yè)務計算任務,同時和 DMA 的 I/O 任務并行,極大地提升系統(tǒng)性能。
但他的劣勢也很明顯,強依賴于硬件的支持
splice
Linux 在 2.6.17 版本引入 splice 系統(tǒng)調用,不再需要硬件支持,同時還實現(xiàn)了兩個文件描述符之間的數(shù)據(jù)零拷貝。
splice 系統(tǒng)調用可以在內核空間的讀緩沖區(qū)(read buffer)和網絡緩沖區(qū)(socket buffer)之間建立管道(pipeline),從而避免了用戶緩沖區(qū)和Socket緩沖區(qū)的 CPU 拷貝操作。
基于 splice 系統(tǒng)調用的零拷貝方式,整個拷貝過程會發(fā)生 2次用戶態(tài)和內核態(tài)的切換,2次數(shù)據(jù)拷貝(2次DMA拷貝),具體過程如下:
用戶進程通過 splice() 函數(shù)向內核(kernel)發(fā)起系統(tǒng)調用,上下文從用戶態(tài)(user space)切換為內核態(tài)(kernel space)。
CPU 利用 DMA 控制器將數(shù)據(jù)從主存或硬盤拷貝到內核空間(kernel space)的讀緩沖區(qū)(read buffer)。
CPU 在內核空間的讀緩沖區(qū)(read buffer)和網絡緩沖區(qū)(socket buffer)之間建立管道(pipeline)。
CPU 利用 DMA 控制器將數(shù)據(jù)從網絡緩沖區(qū)(socket buffer)拷貝到網卡進行數(shù)據(jù)傳輸。
上下文從內核態(tài)(kernel space)切換回用戶態(tài)(user space),splice 系統(tǒng)調用執(zhí)行返回。
splice 拷貝方式也同樣存在用戶程序不能對數(shù)據(jù)進行修改的問題。除此之外,它使用了 Linux 的管道緩沖機制,可以用于任意兩個文件描述符中傳輸數(shù)據(jù),但是它的兩個文件描述符參數(shù)中有一個必須是管道設備
總結
本文簡單介紹了 Linux 中的幾種 Zero-copy 技術,隨著技術的不斷發(fā)展,又出現(xiàn)了諸如:寫時復制、共享緩沖等技術,本文就不再贅述。
廣義的來講,Linux 的 Zero-copy 技術可以歸納成以下三大類:
減少甚至避免用戶空間和內核空間之間的數(shù)據(jù)拷貝:在一些場景下,用戶進程在數(shù)據(jù)傳輸過程中并不需要對數(shù)據(jù)進行訪問和處理,那么數(shù)據(jù)在 Linux 的 Page Cache 和用戶進程的緩沖區(qū)之間的傳輸就完全可以避免,讓數(shù)據(jù)拷貝完全在內核里進行,甚至可以通過更巧妙的方式避免在內核里的數(shù)據(jù)拷貝。這一類實現(xiàn)一般是是通過增加新的系統(tǒng)調用來完成的,比如 Linux 中的 mmap(),sendfile() 以及 splice() 等。
繞過內核的直接 I/O:允許在用戶態(tài)進程繞過內核直接和硬件進行數(shù)據(jù)傳輸,內核在傳輸過程中只負責一些管理和輔助的工作。這種方式其實和第一種有點類似,也是試圖避免用戶空間和內核空間之間的數(shù)據(jù)傳輸,只是第一種方式是把數(shù)據(jù)傳輸過程放在內核態(tài)完成,而這種方式則是直接繞過內核和硬件通信,效果類似但原理完全不同。
內核緩沖區(qū)和用戶緩沖區(qū)之間的傳輸優(yōu)化:這種方式側重于在用戶進程的緩沖區(qū)和操作系統(tǒng)的頁緩存之間的 CPU 拷貝的優(yōu)化。這種方法延續(xù)了以往那種傳統(tǒng)的通信方式,但更靈活。
作者:十三
鏈接:https://www.cnblogs.com/hystrix/p/15213700.html
