為什麼要零拷貝?
1、基本概念
1.1、 DMA控制器
1.2、 CPU使用者态和核心态
2、零拷貝
2.1、普通拷貝
2.2、mmap零拷貝
2.3、sendfile零拷貝
總結
傳統的IO拷貝在計算機中拷貝次數太多,速度太慢,零拷貝可以減少拷貝次數,增加系統性能。另外,零拷貝并不是指沒有進行檔案的拷貝,隻是減少了拷貝的次數。
首先需要知道計算機硬體的讀寫速度,大概如下:
CPU高速緩存屬于速度最快的,這裡可以認為是飛機的速度。
網卡的速度,可以認為是汽車速度。
硬碟可以認為是走路的速度。
那假如把硬碟的檔案通過網卡發送出去,普通IO,CPU高速緩存要向網卡寫資料,即使CPU的速度再快,也會被其他兩個裝置影響。是以CPU需要一個小弟DMA控制器。

直接記憶體通路(DMA)是一種完全由硬體執行I/O交換的工作方式。在這種方式中,DMA控制器從CPU完全接管對總線的控制,資料交換不經過CPU,而直接在記憶體和I/O裝置之間進行 。DMA方式一般用于高速傳送成組資料。DMA控制器将向記憶體發出位址和控制信号,修改位址,對傳送的字的個數計數,并且以中斷方式向CPU報告傳送操作的結束。
DMA方式的主要優點是速度快。由于CPU根本不參加傳送操作,是以就省去了CPU取指令、取數、送數等操作。在資料傳送過程中,沒有儲存現場、恢複現場之類的工作。記憶體位址修改、傳送字個數的計數等等,也不是由軟體實作,而是用硬體線路直接實作的。是以DMA方式能滿足高速I/O裝置的要求,也有利于CPU效率的發揮。
世界上的人本來就是不平等的,這就好比,你老婆能管你的錢,但是你隻能悶頭掙錢一樣。程式也是如此,有的程式權限很高,可以通路計算機的任何資源,但是有的程式權限就低,隻能通路部分資源。這兩個類型的程式,就可以映射為CPU的使用者态和核心态。友善記憶可以這麼了解,核心态是計算機的核心,可以通路計算機的任何資源,如網卡、硬碟。但是為了安全,CPU不能讓使用者程式肆無忌憚的通路計算機的任何資源,這樣如果使用者程式不穩定可能會造成系統崩潰,是以才有的使用者态。
核心态:cpu可以通路記憶體的所有資料,包括外圍裝置,例如硬碟,網卡,cpu也可以将自己從一個程式切換到另一個程式。
使用者态:隻能受限的通路記憶體,且不允許通路外圍裝置,占用cpu的能力被剝奪,cpu資源可以被其他程式擷取。
是以,在CPU想要讀取硬碟檔案的時候,需要從使用者态切換為核心态,才有權限。讀取完成之後,為了程式安全,需要從核心态切換為使用者态。
在普通的拷貝時,大概流程如下
cpu切換到核心态,先到核心态查詢核心緩沖區,如果核心緩沖區有,則可以直接拷貝到使用者空間中。
如果核心緩沖區沒有,則CPU會讓DMA加載到核心空間中。這裡就會有一次DMA拷貝。
拷貝到核心緩沖區之後,CPU将會從核心緩沖區拷貝走。這是一次CPU拷貝。拷貝完成切換到使用者态。
寫的時候,再次切換到核心态。切換完成之後,寫到socket緩沖區。寫完之後,切換到使用者态。
DMA通過異步的方式将socket緩沖區的資料通過網卡發送到對端。
總結這種普通的IO。總共有4次CPU切換(上圖藍色)分别是讀2次、寫2次。4次檔案拷貝,分别是:
檔案從硬碟到核心空間
核心空間到CPU
CPU到socket緩沖區
socket緩沖區到網卡。
mmap是零拷貝的一種方式通過虛拟記憶體的方式實作。也就是說使用者空間和核心空間使用同一個實體位址。這樣,檔案就不在需要經過使用者空間。可以從核心緩沖區直接複制到socket緩沖區。減少了一次檔案拷貝。
sendfile函數在兩個檔案描述符之間傳遞資料(完全在核心中操作),進而避免了核心緩沖區和使用者緩沖區之間的資料拷貝,效率很高,被稱為零拷貝
系統調用 sendfile() 通過 DMA 把硬碟資料拷貝到核心緩沖區,然後資料被核心直接拷貝到另外一個與 socket 相關的 socket緩沖區。這裡沒有 使用者态和核心态 之間的切換,在核心中直接完成了從一個 緩沖區 到另一個緩沖區的拷貝。
DMA 把資料從核心緩沖區 直接拷貝給協定棧,沒有切換,也不需要資料從使用者态和核心态,因為資料就在 核心裡。
零拷貝并不是沒有拷貝,是指減少拷貝的次數。有兩種方式mmap和sendfile。
mmap 适合小資料量讀寫,sendFile 适合大檔案傳輸。(這個并沒有查到詳細理論依據,如果您有線索,歡迎留言)
mmap 需要 4 次上下文切換,3 次資料拷貝;sendFile 需要 3 次上下文切換,最少 2 次資料拷貝。
sendFile 可以利用 DMA 方式,減少 CPU 拷貝,mmap 則不能(必須從核心拷貝到 Socket 緩沖區)。
如果有誤,歡迎指正。