Linux下的零拷贝
Reference:??https://segmentfault.com/a/1190000011989008
?
零拷貝是什么?
維基百科對“零拷貝”是這樣描述的:
"Zero-copy" describes computer operations in which the CPU does not perform the task of copying data from one memory area to another. This is frequently used to save CPU cycles and memory bandwidth when transmitting a file over a network.
“零拷貝” 描述的是CPU不執行拷貝數據從一塊內存區域到另一塊區域的任務的計算機操作。它通常用于在網絡上傳輸文件時節省CPU周期和內存帶寬。簡單來說,零拷貝就是一種避免 CPU 將數據從一塊存儲拷貝到另外一塊存儲的技術。
為什么需要零拷貝技術?
通常我們會有這樣的需求:將本地磁盤上的一個文件通過網絡發送給遠端的另一個服務。在傳統的I/O中,我們通過一張圖來看一下操作系統都會發生什么:
發出read()系統調用,這時處理器會從用戶空間切換至內核空間;
向磁盤請求數據;
通過DMA將文件從磁盤上讀取到內核空間緩沖區;
read()系統調用返回,將數據從內核空間緩沖區拷貝至用戶空間緩沖區,這時候處理器會從內核空間切換至用戶空間;
發出write()系統調用,并將數據從用戶空間緩沖區拷貝至目標socket 在內核空間的緩沖區,這時候處理器會從用戶空間切換至內核空間;
write()調用返回;
通過DMA將數據從內核空間緩沖區中拷貝至協議引擎(該操作是獨立且異步的)。
總的來說:傳統的I/O操作在整個過程中將會產生4次上下文切換和4次數據拷貝。
Q:有人可能會問, 為什么write()調用會先返回,難道他會在數據傳輸前返回?
A:事實上調用的返回并不保證數據被傳輸,甚至他并不保證傳輸的開始,只是意味著以太網驅動程序在其傳輸隊列中有空位,并已經接受我們的將要傳輸的數據。在我們之前很可能還有很多數據包在排除。除非驅動程序或硬件實現優先級環或隊列,否則數據都將以先進先出的方式被傳輸。
了解了傳統I/O的操作后,我們再來觀察一下整個過程,我們會注意到第二次和第三次數據拷貝是完全沒有意義的,應用程序僅僅緩存了一下數據就又原封不動的把它發送給了目標socket 緩沖區。而且這兩次拷貝是需要CPU全程參與的,從操作系統的角度來說,如果 CPU 一直被占用著去執行這項簡單的任務,那么這將會是很浪費資源的;如果有其他比較簡單的系統部件可以代勞這件事情,從而使得 CPU 解脫出來可以做其他的事情,那么系統資源的利用則會更加有效。
“零拷貝”正是通過消除這些多余的拷貝來提升性能的。在數據傳輸的過程中,避免數據在內核空間緩沖區和用戶空間緩沖區之間進行拷貝,以及數據在內核空間緩沖區內的CPU拷貝。
零拷貝的實現機制
Linux 中提供類似的系統調用主要有 sendfile()、mmap() 和splice()(本文對該系統調用暫不做討論)。
通過sendfile()實現的零拷貝
sendfile系統調用在內核版本2.1中被引入,目的是簡化通過網絡在兩個本地文件之間進行的數據傳輸過程。sendfile系統調用的引入,不僅減少了數據復制,還減少了上下文切換的次數。為了更好的說明,請看下圖:
發出sendfile()系統調用,這時處理器會從用戶空間切換至內核空間;
向磁盤請求數據;
通過DMA將文件從磁盤上讀取到內核空間緩沖區;
將數據從內核空間緩沖區拷貝到目標socket緩沖區;
Sendfile()返回,這時處理器從內核空間切換至用戶空間;
通過DMA將數據從目標socket緩沖區拷貝至協議引擎。
總結一下這種實現,整個過程產生了2次上下文切換和3次數據拷貝(其中2次DMA拷貝和1次CPU拷貝)。
該實現雖然減少了2次上下文切換,但仍然還有1次CPU拷貝。那這次拷貝是不是也可以省掉呢?答案是肯定的。但是需要底層操作系統的一些支持。那就是帶有DMA收集功能的sendfile實現的零拷貝。
帶有DMA收集功能的sendfile實現的零拷貝
從Linux2.4開始,操作系統底層提供了帶有scatter/gather的DMA來從內核空間緩沖區中將數據讀取到協議引擎中。這就意味著等待傳輸的數據不需要在連續存儲器中,它可以分散在不同的內存位置。那這樣一來,從文件中讀出的數據就不必拷貝至目標socket的緩沖區中,只需要將緩沖區描述符添加到目標socket的緩沖區中,DMA收集操作會根據緩沖區描述符中的信息將內核空間緩沖區中的數據讀取到協議引擎。這種方法不僅減少了上下文切換、還減少了由CPU參與的數據拷貝。為了更好的理解這種方法所涉及的操作,請看下圖:
發出sendfile()系統調用,處理器從用戶空間切換至內核空間;
通過DMA將數據copy至內核空間緩沖區;
將數據在內核空間緩沖區的地址和偏移量拷貝至目標socket的緩沖區;
Sendfile()返回,處理器從內核空間切換至用戶空間。
帶有scatter/gather 功能的DMA將數據直接從內核緩沖區讀取到協議引擎,從而消除了最后一次CPU拷貝。
總結一下,這種方法產生了2次上下文切換和2次數據拷貝。
這時有人可能會問,如果我把數據從磁盤上讀出來后,再編輯一下,再發送出去,以上所說的零拷貝豈不是不能實現?
對于該問題,Linux提供了mmap來實現。
通過mmap實現的零拷貝
mmap(內存映射):mmap操作提供了一種機制,讓用戶程序直接訪問設備內存,這種機制,相比較在用戶空間和內核空間互相拷貝數據,效率更高。
發出mmap()系統調用,處理器從用戶空間切換至內核空間。
向磁盤請求數據;
通過DMA將數據從磁盤拷貝至內核空間緩沖區;
mmap()調用返回,這時候用戶程序和操作系統共享這個緩沖區,不需要再將數據從kernel buffer 拷貝至 user buffer,處理器從內核空間切換至用戶空間;
用戶邏輯處理;
發出write()系統調用,將數據從內核空間緩沖區拷貝至目標socket緩沖區,這時處理器從用戶空間切換至內核空間;
write()調用返回,處理器從內核空間切換至用戶空間;
通過DMA將數據拷貝至協議引擎。
總結一下:這種方法將產生4次上下文切換和3次數據拷貝。
至此,零拷貝技術就介紹完了。本文所提及的零拷貝技術都是需要底層操作系統支持的,同時,零拷貝技術一直是在不斷地發展和完善當中的,本文并沒有涵蓋 Linux 上出現的所有零拷貝技術。
轉載于:https://www.cnblogs.com/skying555/p/11122072.html
總結
以上是生活随笔為你收集整理的Linux下的零拷贝的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Zjoi2010排列计数Perm
- 下一篇: -Git Linux vi/vim 命令