零拷貝 Zero-Copy:高效數據傳輸的利器

今天要帶大家走進一個神奇的技術領域 —— 零拷貝(Zero-Copy)。在數據如洪流般湧動的互聯網時代,零拷貝就像是一位高效的數據魔法師,它能極大地提升系統性能,讓數據傳輸變得前所未有的順暢。想知道它是如何施展魔法的嗎?那就跟我一起深入探索零拷貝的奇妙世界吧!

一、零拷貝的概念

1.1 概述

零複製(英語:Zero-copy;也譯零拷貝)技術是指計算機執行操作時,CPU 不需要先將數據從某處內存複製到另一個特定區域。這種技術通常用於通過網絡傳輸文件時節省 CPU 週期和內存帶寬。

零拷貝字面上的意思包括兩個,“零” 和 “拷貝”:

零拷貝指在進行數據 IO 時,數據在用戶態下經歷了零次 CPU 拷貝,並非不拷貝數據。通過減少數據傳輸過程中 內核緩衝區和用戶進程緩衝區 間不必要的 CPU 數據拷貝 與 用戶態和內核態的上下文切換次數,降低 CPU 在這兩方面的開銷,釋放 CPU 執行其他任務,更有效的利用系統資源,提高傳輸效率,同時還減少了內存的佔用,也提升應用程序的性能。

由於零拷貝在內核空間中完成所有的內存拷貝,可以最大化使用 socket 緩衝區的可用空間,從而提高了一次系統調用中處理的數據量,進一步降低了上下文切換次數。零拷貝技術基於 PageCache,而 PageCache 緩存了最近訪問過的數據,提升了訪問緩存數據的性能,同時,爲了解決機械磁盤尋址慢的問題,它還協助 IO 調度算法實現了 IO 合併與預讀(這也是順序讀比隨機讀性能好的原因),這進一步提升了零拷貝的性能。

1.2 工作原理

操作系統某些組件(例如驅動程序、文件系統和網絡協議棧)若採用零複製技術,則能極大地增強了特定應用程序的性能,並更有效地利用系統資源。通過使 CPU 得以完成其他而非將機器中的數據複製到另一處的任務,性能也得到了增強。另外,零複製操作減少了在用戶空間與內核空間之間切換模式的次數。

舉例來說,如果要讀取一個文件並通過網絡發送它,傳統方式下每個讀 / 寫週期都需要複製兩次數據和切換兩次上下文,而數據的複製都需要依靠 CPU。通過零複製技術完成相同的操作,上下文切換減少到兩次,並且不需要 CPU 複製數據。

零複製協議對於網絡鏈路容量接近或超過 CPU 處理能力的高速網絡尤爲重要。在這種網絡下,CPU 幾乎將所有時間都花在複製要傳送的數據上,因此將成爲使通信速率低於鏈路容量的瓶頸。

(1) 內核空間 & 用戶空間

我們電腦上跑着的應用程序,其實是需要經過操作系統,才能做一些特殊操作,如磁盤文件讀寫、內存的讀寫等等。因爲這些都是比較危險的操作,不可以由應用程序亂來,只能交給底層操作系統來。

因此,操作系統爲每個進程都分配了內存空間,一部分是用戶空間,一部分是內核空間。內核空間是操作系統內核訪問的區域,是受保護的內存空間,而用戶空間是用戶應用程序訪問的內存區域。以 32 位操作系統爲例,它會爲每一個進程都分配了 4G(2 的 32 次方) 的內存空間。

(2) 用戶態 & 內核態

(3) 上下文切換

什麼是 CPU 上下文?

CPU 寄存器,是 CPU 內置的容量小、但速度極快的內存。而程序計數器,則是用來存儲 CPU 正在執行的指令位置、或者即將執行的下一條指令位置。它們都是 CPU 在運行任何任務前,必須的依賴環境,因此叫做 CPU 上下文。

什麼是 CPU 上下文切換?

它是指,先把前一個任務的 CPU 上下文(也就是 CPU 寄存器和程序計數器)保存起來,然後加載新任務的上下文到這些寄存器和程序計數器,最後再跳轉到程序計數器所指的新位置,運行新任務。

一般我們說的上下文切換,就是指內核(操作系統的核心)在 CPU 上對進程或者線程進行切換。進程從用戶態到內核態的轉變,需要通過系統調用來完成。系統調用的過程,會發生 CPU 上下文的切換。

CPU 寄存器裏原來用戶態的指令位置,需要先保存起來。接着,爲了執行內核態代碼,CPU 寄存器需要更新爲內核態指令的新位置。最後纔是跳轉到內核態運行內核任務。

(4) 虛擬內存

現代操作系統使用虛擬內存,即虛擬地址取代物理地址,使用虛擬內存可以有 2 個好處:

正是多個虛擬內存可以指向同一個物理地址,可以把內核空間和用戶空間的虛擬地址映射到同一個物理地址,這樣的話,就可以減少 IO 的數據拷貝次數啦,示意圖如下:

(5)DMA 技術

DMA,英文全稱是 Direct Memory Access,即直接內存訪問。DMA 本質上是一塊主板上獨立的芯片,允許外設設備和內存存儲器之間直接進行 IO 數據傳輸,其過程不需要 CPU 的參與。

我們一起來看下 IO 流程,DMA 幫忙做了什麼事情

  1. 用戶應用進程調用 read 函數,向操作系統發起 IO 調用,進入阻塞狀態,等待數據返回。

  2. CPU 收到指令後,對 DMA 控制器發起指令調度。

  3. DMA 收到 IO 請求後,將請求發送給磁盤;

  4. 磁盤將數據放入磁盤控制緩衝區,並通知 DMA

  5. DMA 將數據從磁盤控制器緩衝區拷貝到內核緩衝區。

  6. DMA 向 CPU 發出數據讀完的信號,把工作交換給 CPU,由 CPU 負責將數據從內核緩衝區拷貝到用戶緩衝區。

  7. 用戶應用進程由內核態切換回用戶態,解除阻塞狀態

可以發現,DMA 做的事情很清晰啦,它主要就是幫忙 CPU 轉發一下 IO 請求,以及拷貝數據。爲什麼需要它的?

主要就是效率,它幫忙 CPU 做事情,這時候,CPU 就可以閒下來去做別的事情,提高了 CPU 的利用效率。大白話解釋就是,CPU 老哥太忙太累啦,所以他找了個小弟(名叫 DMA) ,替他完成一部分的拷貝工作,這樣 CPU 老哥就能着手去做其他事情。

二、零拷貝技術詳解

2.1 傳統 I/O 與零拷貝對比

(1) 傳統的 IO 流程,包括 read 和 write 的過程

  1. 用戶應用進程調用 read 函數,向操作系統發起 IO 調用,上下文從用戶態轉爲內核態(切換 1)

  2. DMA 控制器把數據從磁盤中,讀取到內核緩衝區。

  3. CPU 把內核緩衝區數據,拷貝到用戶應用緩衝區,上下文從內核態轉爲用戶態(切換 2),read 函數返回

  4. 用戶應用進程通過 write 函數,發起 IO 調用,上下文從用戶態轉爲內核態(切換 3)

  5. CPU 將用戶緩衝區中的數據,拷貝到 socket 緩衝區

  6. DMA 控制器把數據從 socket 緩衝區,拷貝到網卡設備,上下文從內核態切換回用戶態(切換 4),write 函數返回

從流程圖可以看出,傳統 IO 的讀寫流程,包括了 4 次上下文切換(4 次用戶態和內核態的切換),4 次數據拷貝(兩次 CPU 拷貝以及兩次的 DMA 拷貝)。

(2) 零拷貝

用戶態直接 I/O: 應用程序直接訪問硬件存儲,操作系統只是輔助數據傳輸,這種方式依舊存在上下文切換,只不過硬件的數據不經過內核緩衝區。因此直接 IO 不存在內核空間到用戶空間的 CPU 拷貝。如下圖:

用戶通過直接 IO 使用用戶態的庫函數直接訪問硬件設備。數據跨過內核傳輸。如果內核極大提高性能,用戶態直接 IO 只能適用於不需要內核緩衝區的應用程序,這寫應用程序通常在進程地址空間有自己的數據緩衝機制,稱爲自緩存應用程序。如數據庫管理系統。其次,這種零拷貝機制會直接操作磁盤 I/O,由於 CPU 和磁盤 I/O 之間的執行時間差距,會造成大量資源浪費,解決方案是配合異步 IO。

傳統 I/O 方式在進行數據傳輸時,需要經過多次數據拷貝和上下文切換,代價較大。例如,傳統 IO 讀取數據並通過網絡發送的流程中,read () 調用導致上下文從用戶態切換到內核態,內核通過 DMA 引擎將數據從文件讀取到內核空間的緩衝區,再從內核緩衝區拷貝到用戶緩衝區,read () 方法返回又導致上下文從內核態切換到用戶態;send () 調用同樣會引起上下文切換,且數據需要從用戶空間重新拷貝到內核空間緩衝區,最後通過 DMA 引擎將數據從內核緩衝區傳輸到協議引擎緩衝區。傳統 IO 方式,一共在用戶態空間與內核態空間之間發生了 4 次上下文的切換,4 次數據的拷貝過程,其中包括 2 次 DMA 拷貝和 2 次 I/O 拷貝。

而零拷貝技術則致力於減少這些拷貝次數和上下文切換。比如使用 MMap 可以把 IO 執行流程優化爲只需要三次拷貝和四次上下文切換,從而提升程序整體的執行效率,並且節省了程序的內存空間。在 Linux 操作系統中 sendFile () 是一個系統調用函數,用於高效地將文件數據從內核空間直接傳輸到網絡套接字上,實現零拷貝技術,減少 CPU 上下文切換以及內存複製操作,提高文件傳輸性能。零拷貝技術可以減少數據拷貝和共享總線操作的次數,消除傳輸數據在存儲器之間不必要的中間拷貝次數,有效地提高數據傳輸效率。

無論是傳統的 IO 拷貝方式還是引入了零拷貝,2 次 DMA Copy 都是少不了的,因爲兩次 DMA 都是依賴硬件完成的,下面從 CPU 拷貝次數,DMA 拷貝次數,以及系統調用幾個方面總結上述 io 拷貝的差別:

2.2 零拷貝的實現方式

零拷貝並不是沒有拷貝數據,而是減少用戶態 / 內核態的切換次數以及 CPU 拷貝的次數。零拷貝實現有多種方式,分別是:

⑴mmap+write 實現的零拷貝

前面介紹了虛擬內存,可以把內核空間和用戶空間的虛擬地址映射到同一個物理地址,從而減少數據拷貝次數!mmap 就是用了虛擬內存這個特點,它將內核中的讀緩衝區與用戶空間的緩衝區進行映射,所有的 IO 都在內核中完成。mmap+write 實現的零拷貝流程如下:

  1. 用戶進程通過 mmap 方法向操作系統內核發起 IO 調用,上下文從用戶態切換爲內核態。

  2. CPU 利用 DMA 控制器,把數據從硬盤中拷貝到內核緩衝區。

  3. 上下文從內核態切換回用戶態,mmap 方法返回。

  4. 用戶進程通過 write 方法向操作系統內核發起 IO 調用,上下文從用戶態切換爲內核態。

  5. CPU 將內核緩衝區的數據拷貝到的 socket 緩衝區。

  6. CPU 利用 DMA 控制器,把數據從 socket 緩衝區拷貝到網卡,上下文從內核態切換回用戶態,write 調用返回。

  7. 可以發現,mmap+write 實現的零拷貝,I/O 發生了 4 次用戶空間與內核空間的上下文切換,以及 3 次數據拷貝。其中 3 次數據拷貝中,包括了 2 次 DMA 拷貝和 1 次 CPU 拷貝。

mmap 是將讀緩衝區的地址和用戶緩衝區的地址進行映射,內核緩衝區和應用緩衝區共享,所以節省了一次 CPU 拷貝‘’並且用戶進程內存是虛擬的,只是映射到內核的讀緩衝區,可以節省一半的內存空間。

(2)sendfile 實現的零拷貝

sendfile 是 Linux2.1 內核版本後引入的一個系統調用函數 sendfile 表示在兩個文件描述符之間傳輸數據,它是在操作系統內核中操作的,避免了數據從內核緩衝區和用戶緩衝區之間的拷貝操作,因此可以使用它來實現零拷貝。

sendfile 實現的零拷貝流程如下:

  1. 用戶進程發起 sendfile 系統調用,上下文(切換 1)從用戶態轉向內核態

  2. DMA 控制器,把數據從硬盤中拷貝到內核緩衝區。

  3. CPU 將讀緩衝區中數據拷貝到 socket 緩衝區

  4. DMA 控制器,異步把數據從 socket 緩衝區拷貝到網卡,

  5. 上下文(切換 2)從內核態切換回用戶態,sendfile 調用返回。

可以發現,sendfile 實現的零拷貝,I/O 發生了 2 次用戶空間與內核空間的上下文切換,以及 3 次數據拷貝。其中 3 次數據拷貝中,包括了 2 次 DMA 拷貝和 1 次 CPU 拷貝。那能不能把 CPU 拷貝的次數減少到 0 次呢?有的,即帶有 DMA 收集拷貝功能的 sendfile!

(3)sendfile+DMA scatter/gather 實現的零拷貝

linux 2.4 版本之後,對 sendfile 做了優化升級,引入 SG-DMA 技術,其實就是對 DMA 拷貝加入了 scatter/gather 操作,它可以直接從內核空間緩衝區中將數據讀取到網卡。使用這個特點搞零拷貝,即還可以多省去一次 CPU 拷貝。sendfile+DMA scatter/gather 實現的零拷貝流程如下:

  1. 用戶進程發起 sendfile 系統調用,上下文(切換 1)從用戶態轉向內核態

  2. DMA 控制器,把數據從硬盤中拷貝到內核緩衝區。

  3. CPU 把內核緩衝區中的文件描述符信息(包括內核緩衝區的內存地址和偏移量)發送到 socket 緩衝區

  4. DMA 控制器根據文件描述符信息,直接把數據從內核緩衝區拷貝到網卡

  5. 上下文(切換 2)從內核態切換回用戶態,sendfile 調用返回。

可以發現,sendfile+DMA scatter/gather 實現的零拷貝,I/O 發生了 2 次用戶空間與內核空間的上下文切換,以及 2 次數據拷貝。其中 2 次數據拷貝都是包 DMA 拷貝。這就是真正的 零拷貝(Zero-copy) 技術,全程都沒有通過 CPU 來搬運數據,所有的數據都是通過 DMA 來進行傳輸的。

(4)splice 方式

splice 系統調用是 Linux 在 2.6 版本引入的,其不需要硬件支持,並且不再限定於 socket 上,實現兩個普通文件之間的數據零拷貝,splice 系統調用可以在內核緩衝區和 socket 緩衝區之間建立管道來傳輸數據,避免了兩者之間的 CPU 拷貝操作。

splice 也有一些侷限,它的兩個文件描述符參數中有一個必須是管道設備。

三、零拷貝的優勢體現

⑴顯著提高性能

減少 CPU 佔用率:在傳統 I/O 操作中,數據需要在不同的緩衝區之間多次拷貝,這會使 CPU 頻繁參與數據搬運工作。例如,從磁盤讀取文件發送到網絡的過程中,數據要從磁盤緩衝區拷貝到內核緩衝區,再到用戶緩衝區,最後到網絡緩衝區,每次拷貝都需要 CPU 調度。而零拷貝技術減少了這些中間的拷貝環節,使 CPU 能從繁瑣的數據搬運任務中解脫出來,從而將更多的資源用於處理業務邏輯等其他重要事務。

降低系統調用次數:傳統 I/O 在數據傳輸過程中會涉及大量的系統調用,每次系統調用都伴隨着用戶態和內核態之間的上下文切換。上下文切換是一個相對耗時的操作,會消耗 CPU 時間。零拷貝技術通過優化數據傳輸路徑,減少了這種不必要的系統調用次數,進而提高了系統的整體性能。

⑵提升內存帶寬利用率

避免重複數據佔用內存帶寬:傳統 I/O 的多次拷貝操作會使同一份數據在內存中反覆傳輸,佔用了寶貴的內存帶寬。例如,當數據從內核緩衝區複製到用戶緩衝區,再從用戶緩衝區複製回內核緩衝區(如 Socket 緩衝區)時,這些重複的傳輸過程會消耗內存帶寬。零拷貝技術避免了這些多餘的拷貝,讓內存帶寬可以被更有效地用於其他數據的傳輸或處理。

優化內存數據流向:零拷貝技術能夠使數據在內存中以更高效的方式流動。例如,通過直接在內核空間中完成數據從文件讀取到網絡發送的過程,而不經過用戶空間的緩衝區,這樣就避免了數據在用戶空間和內核空間之間的來回穿梭,使得內存帶寬得到更合理的利用。

⑶增加系統吞吐量

加快數據傳輸速度:由於減少了數據拷貝的次數和系統調用的開銷,數據可以更快地從數據源傳輸到目的地。例如,在網絡文件傳輸場景中,零拷貝技術可以讓文件內容更快速地從磁盤通過網絡發送出去,單位時間內能夠傳輸的數據量增加,從而提高了系統的吞吐量。

高效處理大數據量傳輸:對於處理大量數據的應用場景,如大數據分析平臺中的數據加載、分佈式存儲系統中的數據複製等,零拷貝技術的優勢更加明顯。它能夠在不造成性能瓶頸的情況下,高效地處理大量數據的傳輸,確保系統能夠快速地完成數據的流轉。

⑷降低延遲

減少數據傳輸路徑中的等待時間:傳統 I/O 中數據的多次拷貝和系統調用導致數據在傳輸過程中有較多的等待時間。例如,每次在緩衝區之間拷貝數據時,數據需要等待拷貝操作完成才能進入下一個環節。零拷貝技術縮短了數據傳輸的路徑,減少了這些等待時間,使得數據能夠更快地到達目標位置,從而降低了延遲。

優化實時性要求高的應用場景:在一些對實時性要求較高的應用場景中,如實時音視頻流傳輸、金融交易系統中的數據同步等,降低延遲至關重要。零拷貝技術能夠滿足這些場景對低延遲的要求,提升用戶體驗和系統的響應性能。

⑸提高數據安全性和系統穩定性

減少數據在內存中的暴露環節:在傳統 I/O 中,數據在多個緩衝區之間的拷貝增加了數據在內存中暴露的機會,可能會增加數據被篡改或泄露的風險。零拷貝技術減少了數據在不同緩衝區之間的移動,降低了數據暴露的風險,提高了數據的安全性。

降低系統因頻繁拷貝導致的故障風險:頻繁的數據拷貝可能會導致系統出現一些不可預見的問題,如緩衝區溢出、數據不一致等。零拷貝技術通過簡化數據傳輸流程,減少了這些潛在的風險,從而提高了系統的穩定性。

四、零拷貝的應用場景

⑴網絡文件傳輸與服務器應用

Web 服務器:像 Nginx 等高性能 Web 服務器在處理大量靜態文件請求時,使用零拷貝技術可以快速將文件內容發送給客戶端,減少響應時間和服務器資源消耗。當用戶請求一個靜態文件(如圖片、HTML 文件等)時,服務器可以直接將文件從磁盤讀取到內核緩衝區,然後通過零拷貝技術將數據傳輸到網絡,避免了傳統方式下數據在用戶空間和內核空間之間的多次拷貝。

文件下載服務:在提供大文件下載的服務中,零拷貝能夠顯著提高文件傳輸速度,降低服務器的 CPU 和內存開銷。例如,一些雲存儲服務提供商在實現文件下載功能時,採用零拷貝技術可以讓用戶更快地獲取到文件,同時減少服務器的資源佔用,提高系統的併發處理能力。

分佈式文件系統:在分佈式文件系統中,節點之間需要頻繁地傳輸大量文件數據。零拷貝技術可以優化數據傳輸過程,提高文件同步和數據複製的效率。比如 Hadoop 分佈式文件系統中,數據節點之間的數據傳輸可以利用零拷貝技術減少數據拷貝次數,加快數據傳輸速度。

⑵數據庫系統

數據備份與恢復:數據庫在進行數據備份和恢復操作時,往往需要處理大量的數據。零拷貝技術可以減少數據從磁盤讀取到備份介質或從備份介質恢復到磁盤時的數據拷貝次數,提高備份和恢復的速度,降低對數據庫系統性能的影響。例如,在 MySQL 數據庫的備份和恢復過程中,使用零拷貝技術可以加快數據的傳輸和處理速度。

日誌處理:數據庫的日誌文件記錄了數據庫的操作記錄和狀態信息,對於數據庫的恢復和故障排查非常重要。在處理日誌文件時,零拷貝技術可以快速地將日誌數據從磁盤讀取到內存中進行處理,或者將處理後的日誌數據快速地寫入磁盤,提高日誌處理的效率。

⑶消息隊列系統

高性能消息傳遞:消息隊列系統需要快速地接收、存儲和轉發消息。零拷貝技術可以減少消息在隊列之間傳輸過程中的數據拷貝次數,提高消息的處理速度和系統的吞吐量。例如,Kafka、RocketMQ 等消息隊列系統都採用了零拷貝技術來優化數據傳輸,提高系統的性能。

流式數據處理:在流式數據處理場景中,消息隊列作爲數據的傳輸管道,需要高效地處理大量的實時數據。零拷貝技術可以確保數據在消息隊列系統中的快速流轉,減少數據處理的延遲,滿足流式數據處理對實時性的要求。

⑷虛擬化和容器化技術

虛擬機間的數據傳輸:在虛擬化環境中,虛擬機之間需要進行數據交換和共享。零拷貝技術可以優化虛擬機之間的數據傳輸過程,減少數據在宿主機和虛擬機之間的拷貝次數,提高數據傳輸的效率和虛擬機的性能。例如,在 VMware、KVM 等虛擬化平臺中,零拷貝技術可以用於虛擬機之間的文件傳輸、網絡通信等場景。

容器間的數據共享:容器化技術中,容器之間也需要進行數據共享和通信。零拷貝技術可以實現容器之間的數據快速共享,減少數據的拷貝和傳輸開銷,提高容器化應用的性能和效率。例如,Docker 容器平臺中,可以使用零拷貝技術來優化容器之間的數據傳輸和共享。

⑸視頻直播與多媒體處理

視頻直播平臺:視頻直播平臺需要實時地將視頻數據傳輸給觀衆,對數據傳輸的速度和效率要求很高。零拷貝技術可以減少視頻數據在採集、編碼、傳輸等環節中的數據拷貝次數,降低延遲,提高視頻的流暢度和質量。例如,一些直播平臺在採集攝像頭或其他視頻源的數據後,直接使用零拷貝技術將數據傳輸到服務器進行編碼和分發,減少了數據處理的時間和資源消耗。

多媒體編輯與處理軟件:在多媒體編輯和處理軟件中,需要對大量的音頻、視頻文件進行讀取、編輯和保存。零拷貝技術可以加快文件的讀取和保存速度,提高軟件的處理效率。例如,在視頻編輯軟件中,使用零拷貝技術可以快速地將視頻文件讀取到內存中進行編輯,然後直接將編輯後的視頻數據保存到磁盤,減少了數據在內存和磁盤之間的拷貝次數。

五、java 提供的零拷貝方式

5.1Java NIO 對 mmap 的支持

Java NIO 有一個 MappedByteBuffer 的類,可以用來實現內存映射。它的底層是調用了 Linux 內核的 mmap 的 API。

public class MmapTest { public static void main(String[] args) { try {
            FileChannel readChannel = FileChannel.open(Paths.get("./jay.txt"), StandardOpenOption.READ);
            MappedByteBuffer data = readChannel.map(FileChannel.MapMode.READ_ONLY, 0, 1024 * 1024 * 40);
            FileChannel writeChannel = FileChannel.open(Paths.get("./siting.txt"), StandardOpenOption.WRITE, StandardOpenOption.CREATE); //數據傳輸 writeChannel.write(data);
            readChannel.close();
            writeChannel.close();
        }catch (Exception e){
            System.out.println(e.getMessage());
        }
    }
}

5.2Java NIO 對 sendfile 的支持

FileChannel 的 transferTo()/transferFrom(),底層就是 sendfile() 系統調用函數。Kafka 這個開源項目就用到它,平時面試的時候,回答面試官爲什麼這麼快,就可以提到零拷貝 sendfile 這個點。

public class SendFileTest { public static void main(String[] args) { try {
            FileChannel readChannel = FileChannel.open(Paths.get("./jay.txt"), StandardOpenOption.READ); long len = readChannel.size(); long position = readChannel.position();
            
            FileChannel writeChannel = FileChannel.open(Paths.get("./siting.txt"), StandardOpenOption.WRITE, StandardOpenOption.CREATE); //數據傳輸 readChannel.transferTo(position, len, writeChannel);
            readChannel.close();
            writeChannel.close();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }
}

5.3 案例分析

使用 Java NIO 中的零拷貝技術的案例分析及代碼實現示例,用於將一個文件從磁盤讀取並通過網絡發送給客戶端。

傳統方式的問題

在傳統的文件傳輸方式中,當從磁盤讀取文件並通過網絡發送時,通常需要進行多次數據拷貝。首先,數據從磁盤讀取到內核緩衝區,然後從內核緩衝區拷貝到用戶空間緩衝區,接着當要通過網絡發送時,又要從用戶空間緩衝區拷貝到內核的 socket 緩衝區,最後從內核 socket 緩衝區發送到網絡。這不僅消耗了大量的 CPU 時間和內存帶寬,還增加了數據傳輸的延遲。

例如,在一個簡單的文件服務器應用中,如果使用傳統的方式處理大量的文件傳輸請求,服務器的 CPU 可能會因爲頻繁的數據拷貝而負載過高,導致響應其他請求變慢,同時也會影響文件傳輸的速度和效率。

零拷貝的優勢

零拷貝技術可以避免這些不必要的數據拷貝操作。在 Java 中,可以使用 FileChannel 的 transferTo 方法來實現零拷貝。通過這種方式,數據可以直接從磁盤文件讀取到網絡通道,減少了數據在用戶空間和內核空間之間的來回拷貝,從而提高了傳輸效率,降低了 CPU 使用率和延遲。

對於大規模文件傳輸場景,如視頻文件分發、大文件下載服務等,零拷貝技術可以顯著提高系統的性能和吞吐量,能夠更快地響應客戶端請求,同時減少服務器資源的消耗。

代碼實現:

import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.FileChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

public class ZeroCopyFileServer {
    public static void main(String[] args) throws IOException {
        // 監聽端口
        int port = 8888;
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.bind(new InetSocketAddress(port));

        while (true) {
            // 等待客戶端連接
            SocketChannel socketChannel = serverSocketChannel.accept();

            // 打開文件並獲取FileChannel
            String filePath = "your_file_path_here"; // 替換爲實際文件路徑
            try (FileInputStream fileInputStream = new FileInputStream(filePath);
                 FileChannel fileChannel = fileInputStream.getChannel()) {

                // 使用零拷貝將文件數據傳輸到網絡通道
                long bytesTransferred = fileChannel.transferTo(0, fileChannel.size(), socketChannel);
                System.out.println("Transferred " + bytesTransferred + " bytes.");
            }
            // 關閉連接
            socketChannel.close();
        }
    }
}
  1. 首先,創建了一個 ServerSocketChannel 並綁定到指定端口 8888,用於監聽客戶端連接。

  2. 在循環中,接受客戶端連接後,打開要傳輸的文件,獲取 FileChannel。

  3. 然後,使用 FileChannel 的 transferTo 方法將文件數據直接傳輸到 SocketChannel(代表與客戶端的連接通道),實現了零拷貝的數據傳輸。

  4. 最後,關閉與客戶端的連接,等待下一個客戶端連接。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/palP5hqYQ5gPmuvRHRUoJg