在nginx、kafka等開源元件的原理和效能調優中,經常會提到零拷貝技術,為了能從原理層面掌握這些常用元件,下面我詳細介紹零拷貝的原理。
在介紹零拷貝之前,還有幾個概念需要介紹,那就是:使用者空間(user space)、核心空間(kernel space)。
使用者空間是指:使用者程式**執行的地方; 核心空間是指:核心**執行的地方,為了安全,它們是隔離的,即使使用者的程式崩潰了,核心也不受影響。當程序執行在核心空間時就處於核心態,當程序執行在使用者空間時就處於使用者態。
核心空間 可以執行任意命令,呼叫系統的一切資源;使用者 只能執行簡單的運算,不能直接呼叫系統資源,必須通過系統介面(又稱 system call),才能向核心發出指令。
通過系統介面,程序可以從使用者空間切換到核心空間,如下圖所示,使用者**呼叫 cat /etc/hosts,從使用者空間切換到核心空間:
操心系統將虛擬空間劃分為兩部分,一部分為核心空間,一部分為使用者空間。針對linux作業系統而言,將最高的1g位元組(從虛擬位址0xc0000000到0xffffffff),供核心使用,稱為核心空間,而將較低的3g位元組(從虛擬位址0x00000000到0xbfffffff),供各個程序使用,稱為使用者空間。每個程序可以通過系統呼叫進入核心,因此,linux核心由系統內的所有程序共享。於是,從具體程序的角度來看,每個程序可以擁有4g位元組的虛擬空間。空間分配如下圖所示:
有了使用者空間和核心空間,整個linux內部結構可以分為三部分,從最底層到最上層依次是:硬體-->核心空間-->使用者空間。如下圖所示:
需要注意的細節問題:
(1) 核心空間中存放的是核心**和資料,而程序的使用者空間中存放的是使用者程式的**和資料。不管是核心空間還是使用者空間,它們都處於虛擬空間中。
(2) linux使用兩級保護機制:0級供核心使用,3級供使用者程式使用。
核心態與使用者態:
(1)當乙個任務(程序)執行系統呼叫而陷入核心**中執行時,稱程序處於核心執行態(核心態)。此時處理器處於特權級最高的(0級)核心**中執行。當程序處於核心態時,執行的核心**會使用當前程序的核心棧。每個程序都有自己的核心棧。
(2)當程序在執行使用者自己的**時,則稱其處於使用者執行態(使用者態)。此時處理器在特權級最低的(3級)使用者**中執行。當正在執行使用者程式而突然被中斷程式中斷時,此時使用者程式也可以象徵性地稱為處於程序的核心態。因為中斷處理程式將使用當前程序的核心棧。
零拷貝指的是,從乙個儲存區域到另乙個儲存區域的 copy 任務沒有cpu 參與。零拷貝通常用於網路檔案傳輸,以減少 cpu 消耗和記憶體頻寬占用,減少使用者空間與 cpu 核心空間的拷貝過程,減少使用者上下文與 cpu 核心上下文間的切換,提高系統效率。
使用者上下文指的是使用者狀態環境,cpu 核心上下文指的是 cpu 核心狀態環境。
零拷貝需要 dma 控制器的協助。dma,direct memory access,直接記憶體訪問,是 cpu的組成部分,其可以在 cpu 核心(算術邏輯運算器alu 等)不參與運算的情況下將資料從乙個位址空間拷貝到另乙個位址空間。
下面均以「將乙個硬碟中的檔案通過網路傳送出去」的過程為例,來詳細詳細分析不同拷貝方式的實現細節。
首先通過應用程式的 read()方法將檔案從硬碟讀取出來,然後再呼叫 send()方法將檔案傳送出去。
該拷貝方式共進行了 4 次使用者空間與核心空間的上下文切換,以及 4 次資料拷貝,其中兩次拷貝存在 cpu 參與。
我們發現乙個很明顯的問題:應用程式的作用僅僅就是乙個資料傳輸的中介,最後將kernel buffer 中的資料傳遞到了 socket buffer。顯然這是沒有必要的。所以就引入了零拷貝。
linux 系統(centos6 及其以上版本)對於零拷貝是通過 sendfile 系統呼叫實現的。
該拷貝方式共進行了 2 次使用者空間與核心空間的上下文切換,以及 3 次資料拷貝,但整個拷貝過程均沒有 cpu 的參與,這就是零拷貝。
我們發現這裡還存在乙個問題:kernel buffer 到 socket buffer 的拷貝需要嗎?kernel buffer 與 socket buffer 有什麼區別呢?dma 控制器所控制的拷貝過程有乙個要求,資料在源頭的存放位址空間必須是連續的。kernel buffer 中的資料無法保證其連續性,所以需要將資料再拷貝到 socket buffer,socket buffer 可以保證了資料的連續性。
這個拷貝過程能否避免呢?可以,只要主機的 dma 支援gather copy 功能,就可以避免由 kernel buffer 到 socket buffer 的拷貝。
由於該拷貝方式是由 dma 完成,與系統無關,所以只要保證系統支援 sendfile 系統呼叫功能即可。
該拷貝方式共進行了 2 次使用者空間與核心空間的上下文切換,以及 2 次資料拷貝,並且整個拷貝過程均沒有 cpu 的參與。
該拷貝方式的系統效率是高了,但與傳統相比,也存在有不足。傳統拷貝中user buffer 中存有資料,因此應用程式能夠對資料進行修改等操作;零拷貝中的 user buffer 中沒有了資料,所以應用程式無法對資料進行操作了。linux 的mmap 零拷貝解決了這個問題。
mmap 零拷貝是對零拷貝的改進。當然,若當前主機的 dma 支援 gather copy,mmap同樣可以實現gather copy dma 的零拷貝。
該方式與零拷貝的唯一區別是,應用程式與核心共享了 kernel buffer。由於是共享,所以應用程式也就可以操作該 buffer 了。當然,應用程式對於 kernel buffer 的操作,就會引發使用者空間與核心空間的相互切換。
該拷貝方式共進行了 4 次使用者空間與核心空間的上下文切換,以及 2 次資料拷貝,並且整個拷貝過程均沒有 cpu 的參與。雖然較之前面的零拷貝增加了兩次上下文切換,但應用程式可以對資料進行修改了。
零拷貝實現原理
從 作業系統中直接操作記憶體,netty底層就是零拷貝,netty就是具有零拷貝功能 netty的零拷貝主要體現在三個方面 1 netty的接收和傳送bytebuffer採用direct buffers,使用堆外直接記憶體進行scoket讀寫,不需要進行位元組緩衝區的二次拷貝.如果使用傳統的堆記憶體...
零拷貝詳解(上)
拷貝也就是把磁碟或網路中的a檔案中拷到b檔案中。那麼是誰來執行從磁碟中讀取操作和寫入操作的呢,並且他們讀取完以後是直接就能拷到b檔案中,還是需要進行一些複雜的處理?在這裡將會對比下傳統的資料傳輸和零拷貝方式的傳輸,這兩者有什麼區別。在這之前先了解使用者態和核心態這2個概念 再然後讓我們了解下什麼是上...
sendfile 實現零拷貝詳解
2013年11月18日 11 17 供稿中心 網際網路運營部 摘要 linux的sendfile 系統呼叫 伺服器響應乙個http請求的步驟如下 1 把磁碟檔案讀入核心緩衝區 2 從核心緩衝區讀到記憶體 3 處理 靜態資源不需處理 4 傳送到網絡卡的核心緩衝區 傳送快取 5 網絡卡傳送資料 資料從第...