Java零拷貝一步曲——Linux 中的零拷貝技術(shù)

引言

傳統(tǒng)的 Linux 操作系統(tǒng)的標(biāo)準(zhǔn) I/O 接口是基于數(shù)據(jù)拷貝操作的,即 I/O 操作會(huì)導(dǎo)致數(shù)據(jù)在操作系統(tǒng)內(nèi)核地址空間的緩沖區(qū)和應(yīng)用程序地址空間定義的緩沖區(qū)之間進(jìn)行傳輸。這樣做最大的好處是可以減少磁盤(pán) I/O 的操作,因?yàn)槿绻?qǐng)求的數(shù)據(jù)已經(jīng)存放在操作系統(tǒng)的高速緩沖存儲(chǔ)器中,那么就不需要再進(jìn)行實(shí)際的物理磁盤(pán) I/O 操作。但是數(shù)據(jù)傳輸過(guò)程中的數(shù)據(jù)拷貝操作卻導(dǎo)致了極大的 CPU 開(kāi)銷(xiāo),限制了操作系統(tǒng)有效進(jìn)行數(shù)據(jù)傳輸操作的能力。

零拷貝( zero-copy )這種技術(shù)可以有效地改善數(shù)據(jù)傳輸?shù)男阅?,在?nèi)核驅(qū)動(dòng)程序(比如網(wǎng)絡(luò)堆棧或者磁盤(pán)存儲(chǔ)驅(qū)動(dòng)程序)處理 I/O 數(shù)據(jù)的時(shí)候,零拷貝技術(shù)可以在某種程度上減少甚至完全避免不必要 CPU 數(shù)據(jù)拷貝操作。現(xiàn)代的 CPU 和存儲(chǔ)體系結(jié)構(gòu)提供了很多特征可以有效地實(shí)現(xiàn)零拷貝技術(shù),但是因?yàn)榇鎯?chǔ)體系結(jié)構(gòu)非常復(fù)雜,而且網(wǎng)絡(luò)協(xié)議棧有時(shí)需要對(duì)數(shù)據(jù)進(jìn)行必要的處理,所以零拷貝技術(shù)有可能會(huì)產(chǎn)生很多負(fù)面的影響,甚至?xí)?dǎo)致零拷貝技術(shù)自身的優(yōu)點(diǎn)完全喪失。

為什么需要零拷貝技術(shù)

如今,很多網(wǎng)絡(luò)服務(wù)器都是基于客戶(hù)端 - 服務(wù)器這一模型的。在這種模型中,客戶(hù)端向服務(wù)器端請(qǐng)求數(shù)據(jù)或者服務(wù);服務(wù)器端則需要響應(yīng)客戶(hù)端發(fā)出的請(qǐng)求,并為客戶(hù)端提供它所需要的數(shù)據(jù)。隨著網(wǎng)絡(luò)服務(wù)的逐漸普及,video 這類(lèi)應(yīng)用程序發(fā)展迅速。當(dāng)今的計(jì)算機(jī)系統(tǒng)已經(jīng)具備足夠的能力去處理 video 這類(lèi)應(yīng)用程序?qū)蛻?hù)端所造成的重負(fù)荷,但是對(duì)于服務(wù)器端來(lái)說(shuō),它應(yīng)付由 video 這類(lèi)應(yīng)用程序引起的網(wǎng)絡(luò)通信量就顯得捉襟見(jiàn)肘了。而且,客戶(hù)端的數(shù)量增長(zhǎng)迅速,那么服務(wù)器端就更容易成為性能瓶頸。而對(duì)于負(fù)荷很重的服務(wù)器來(lái)說(shuō),操作系統(tǒng)通常都是引起性能瓶頸的罪魁禍?zhǔn)住Ee個(gè)例子來(lái)說(shuō),當(dāng)數(shù)據(jù)“寫(xiě)”操作或者數(shù)據(jù)“發(fā)送”操作的系統(tǒng)調(diào)用發(fā)出時(shí),操作系統(tǒng)通常都會(huì)將數(shù)據(jù)從應(yīng)用程序地址空間的緩沖區(qū)拷貝到操作系統(tǒng)內(nèi)核的緩沖區(qū)中去。操作系統(tǒng)這樣做的好處是接口簡(jiǎn)單,但是卻在很大程度上損失了系統(tǒng)性能,因?yàn)檫@種數(shù)據(jù)拷貝操作不單需要占用 CPU 時(shí)間片,同時(shí)也需要占用額外的內(nèi)存帶寬。

一般來(lái)說(shuō),客戶(hù)端通過(guò)網(wǎng)絡(luò)接口卡向服務(wù)器端發(fā)送請(qǐng)求,操作系統(tǒng)將這些客戶(hù)端的請(qǐng)求傳遞給服務(wù)器端應(yīng)用程序,服務(wù)器端應(yīng)用程序會(huì)處理這些請(qǐng)求,請(qǐng)求處理完成以后,操作系統(tǒng)還需要將處理得到的結(jié)果通過(guò)網(wǎng)絡(luò)適配器傳遞回去。

下邊這一小節(jié)會(huì)跟讀者簡(jiǎn)單介紹一下傳統(tǒng)的服務(wù)器是如何進(jìn)行數(shù)據(jù)傳輸?shù)?,以及這種數(shù)據(jù)傳輸?shù)奶幚磉^(guò)程存在哪些問(wèn)題有可能會(huì)造成服務(wù)器的性能損失

Linux中傳統(tǒng)服務(wù)器進(jìn)行數(shù)據(jù)傳輸?shù)牧鞒?/h2>

Linux  中傳統(tǒng)的 I/O 操作是一種緩沖 I/O,I/O 過(guò)程中產(chǎn)生的數(shù)據(jù)傳輸通常需要在緩沖區(qū)中進(jìn)行多次的拷貝操作。一般來(lái)說(shuō),在傳輸數(shù)據(jù)的時(shí)候,用戶(hù)應(yīng)用程序需要分配一塊大小合適的緩沖區(qū)用來(lái)存放需要傳輸?shù)臄?shù)據(jù)。應(yīng)用程序從文件中讀取一塊數(shù)據(jù),然后把這塊數(shù)據(jù)通過(guò)網(wǎng)絡(luò)發(fā)送到接收端去。用戶(hù)應(yīng)用程序只是需要調(diào)用兩個(gè)系統(tǒng)調(diào)用 read() 和 write() 就可以完成這個(gè)數(shù)據(jù)傳輸操作,應(yīng)用程序并不知曉在這個(gè)數(shù)據(jù)傳輸?shù)倪^(guò)程中操作系統(tǒng)所做的數(shù)據(jù)拷貝操作。對(duì)于 Linux 操作系統(tǒng)來(lái)說(shuō),基于數(shù)據(jù)排序或者校驗(yàn)等各方面因素的考慮,操作系統(tǒng)內(nèi)核會(huì)在處理數(shù)據(jù)傳輸?shù)倪^(guò)程中進(jìn)行多次拷貝操作。在某些情況下,這些數(shù)據(jù)拷貝操作會(huì)極大地降低數(shù)據(jù)傳輸?shù)男阅堋?/p>

當(dāng)應(yīng)用程序需要訪(fǎng)問(wèn)某塊數(shù)據(jù)的時(shí)候,操作系統(tǒng)內(nèi)核會(huì)先檢查這塊數(shù)據(jù)是不是因?yàn)榍耙淮螌?duì)相同文件的訪(fǎng)問(wèn)而已經(jīng)被存放在操作系統(tǒng)內(nèi)核地址空間的緩沖區(qū)內(nèi),如果在內(nèi)核緩沖區(qū)中找不到這塊數(shù)據(jù),Linux 操作系統(tǒng)內(nèi)核會(huì)先將這塊數(shù)據(jù)從磁盤(pán)讀出來(lái)放到操作系統(tǒng)內(nèi)核的緩沖區(qū)里去。如果這個(gè)數(shù)據(jù)讀取操作是由 DMA 完成的,那么在 DMA 進(jìn)行數(shù)據(jù)讀取的這一過(guò)程中,CPU 只是需要進(jìn)行緩沖區(qū)管理,以及創(chuàng)建和處理 DMA ,除此之外,CPU 不需要再做更多的事情,DMA 執(zhí)行完數(shù)據(jù)讀取操作之后,會(huì)通知操作系統(tǒng)做進(jìn)一步的處理。Linux 操作系統(tǒng)會(huì)根據(jù) read() 系統(tǒng)調(diào)用指定的應(yīng)用程序地址空間的地址,把這塊數(shù)據(jù)存放到請(qǐng)求這塊數(shù)據(jù)的應(yīng)用程序的地址空間中去,在接下來(lái)的處理過(guò)程中,操作系統(tǒng)需要將數(shù)據(jù)再一次從用戶(hù)應(yīng)用程序地址空間的緩沖區(qū)拷貝到與網(wǎng)絡(luò)堆棧相關(guān)的內(nèi)核緩沖區(qū)中去,這個(gè)過(guò)程也是需要占用 CPU 的。數(shù)據(jù)拷貝操作結(jié)束以后,數(shù)據(jù)會(huì)被打包,然后發(fā)送到網(wǎng)絡(luò)接口卡上去。在數(shù)據(jù)傳輸?shù)倪^(guò)程中,應(yīng)用程序可以先返回進(jìn)而執(zhí)行其他的操作。之后,在調(diào)用 write() 系統(tǒng)調(diào)用的時(shí)候,用戶(hù)應(yīng)用程序緩沖區(qū)中的數(shù)據(jù)內(nèi)容可以被安全的丟棄或者更改,因?yàn)椴僮飨到y(tǒng)已經(jīng)在內(nèi)核緩沖區(qū)中保留了一份數(shù)據(jù)拷貝,當(dāng)數(shù)據(jù)被成功傳送到硬件上之后,這份數(shù)據(jù)拷貝就可以被丟棄。

從上面的描述可以看出,在這種傳統(tǒng)的數(shù)據(jù)傳輸過(guò)程中,數(shù)據(jù)至少發(fā)生了四次拷貝操作,即便是使用了 DMA 來(lái)進(jìn)行與硬件的通訊,CPU 仍然需要訪(fǎng)問(wèn)數(shù)據(jù)兩次。在 read() 讀數(shù)據(jù)的過(guò)程中,數(shù)據(jù)并不是直接來(lái)自于硬盤(pán),而是必須先經(jīng)過(guò)操作系統(tǒng)的文件系統(tǒng)層。在 write() 寫(xiě)數(shù)據(jù)的過(guò)程中,為了和要傳輸?shù)臄?shù)據(jù)包的大小相吻合,數(shù)據(jù)必須要先被分割成塊,而且還要預(yù)先考慮包頭,并且要進(jìn)行數(shù)據(jù)校驗(yàn)和操作。
圖 1. 傳統(tǒng)使用 read 和 write 系統(tǒng)調(diào)用的數(shù)據(jù)傳輸

image.png

零拷貝(zero copy)技術(shù)概述

什么是零拷貝?

簡(jiǎn)單一點(diǎn)來(lái)說(shuō),零拷貝就是一種避免 CPU 將數(shù)據(jù)從一塊存儲(chǔ)拷貝到另外一塊存儲(chǔ)的技術(shù)。針對(duì)操作系統(tǒng)中的設(shè)備驅(qū)動(dòng)程序、文件系統(tǒng)以及網(wǎng)絡(luò)協(xié)議堆棧而出現(xiàn)的各種零拷貝技術(shù)極大地提升了特定應(yīng)用程序的性能,并且使得這些應(yīng)用程序可以更加有效地利用系統(tǒng)資源。這種性能的提升就是通過(guò)在數(shù)據(jù)拷貝進(jìn)行的同時(shí),允許 CPU 執(zhí)行其他的任務(wù)來(lái)實(shí)現(xiàn)的。零拷貝技術(shù)可以減少數(shù)據(jù)拷貝和共享總線(xiàn)操作的次數(shù),消除傳輸數(shù)據(jù)在存儲(chǔ)器之間不必要的中間拷貝次數(shù),從而有效地提高數(shù)據(jù)傳輸效率。而且,零拷貝技術(shù)減少了用戶(hù)應(yīng)用程序地址空間和操作系統(tǒng)內(nèi)核地址空間之間因?yàn)樯舷挛那袚Q而帶來(lái)的開(kāi)銷(xiāo)。進(jìn)行大量的數(shù)據(jù)拷貝操作其實(shí)是一件簡(jiǎn)單的任務(wù),從操作系統(tǒng)的角度來(lái)說(shuō),如果 CPU 一直被占用著去執(zhí)行這項(xiàng)簡(jiǎn)單的任務(wù),那么這將會(huì)是很浪費(fèi)資源的;如果有其他比較簡(jiǎn)單的系統(tǒng)部件可以代勞這件事情,從而使得 CPU 解脫出來(lái)可以做別的事情,那么系統(tǒng)資源的利用則會(huì)更加有效。綜上所述,零拷貝技術(shù)的目標(biāo)可以概括如下:

避免數(shù)據(jù)拷貝

  • 避免操作系統(tǒng)內(nèi)核緩沖區(qū)之間進(jìn)行數(shù)據(jù)拷貝操作。
  • 避免操作系統(tǒng)內(nèi)核和用戶(hù)應(yīng)用程序地址空間這兩者之間進(jìn)行數(shù)據(jù)拷貝操作。
  • 用戶(hù)應(yīng)用程序可以避開(kāi)操作系統(tǒng)直接訪(fǎng)問(wèn)硬件存儲(chǔ)。
  • 數(shù)據(jù)傳輸盡量讓 DMA 來(lái)做

將多種操作結(jié)合在一起

  • 避免不必要的系統(tǒng)調(diào)用和上下文切換。
  • 需要拷貝的數(shù)據(jù)可以先被緩存起來(lái)。
  • 對(duì)數(shù)據(jù)進(jìn)行處理盡量讓硬件來(lái)做。

前文提到過(guò),對(duì)于高速網(wǎng)絡(luò)來(lái)說(shuō),零拷貝技術(shù)是非常重要的。這是因?yàn)楦咚倬W(wǎng)絡(luò)的網(wǎng)絡(luò)鏈接能力與 CPU 的處理能力接近,甚至?xí)^(guò) CPU 的處理能力。如果是這樣的話(huà),那么 CPU 就有可能需要花費(fèi)幾乎所有的時(shí)間去拷貝要傳輸?shù)臄?shù)據(jù),而沒(méi)有能力再去做別的事情,這就產(chǎn)生了性能瓶頸,限制了通訊速率,從而降低了網(wǎng)絡(luò)鏈接的能力。一般來(lái)說(shuō),一個(gè) CPU 時(shí)鐘周期可以處理一位的數(shù)據(jù)。舉例來(lái)說(shuō),一個(gè) 1 GHz 的處理器可以對(duì) 1Gbit/s 的網(wǎng)絡(luò)鏈接進(jìn)行傳統(tǒng)的數(shù)據(jù)拷貝操作,但是如果是 10 Gbit/s 的網(wǎng)絡(luò),那么對(duì)于相同的處理器來(lái)說(shuō),零拷貝技術(shù)就變得非常重要了。對(duì)于超過(guò) 1 Gbit/s 的網(wǎng)絡(luò)鏈接來(lái)說(shuō),零拷貝技術(shù)在超級(jí)計(jì)算機(jī)集群以及大型的商業(yè)數(shù)據(jù)中心中都有所應(yīng)用。然而,隨著信息技術(shù)的發(fā)展,1 Gbit/s,10 Gbit/s 以及 100 Gbit/s 的網(wǎng)絡(luò)會(huì)越來(lái)越普及,那么零拷貝技術(shù)也會(huì)變得越來(lái)越普及,這是因?yàn)榫W(wǎng)絡(luò)鏈接的處理能力比 CPU 的處理能力的增長(zhǎng)要快得多。傳統(tǒng)的數(shù)據(jù)拷貝受限于傳統(tǒng)的操作系統(tǒng)或者通信協(xié)議,這就限制了數(shù)據(jù)傳輸性能。零拷貝技術(shù)通過(guò)減少數(shù)據(jù)拷貝次數(shù),簡(jiǎn)化協(xié)議處理的層次,在應(yīng)用程序和網(wǎng)絡(luò)之間提供更快的數(shù)據(jù)傳輸方法,從而可以有效地降低通信延遲,提高網(wǎng)絡(luò)吞吐率。零拷貝技術(shù)是實(shí)現(xiàn)主機(jī)或者路由器等設(shè)備高速網(wǎng)絡(luò)接口的主要技術(shù)之一。

現(xiàn)代的 CPU 和存儲(chǔ)體系結(jié)構(gòu)提供了很多相關(guān)的功能來(lái)減少或避免 I/O 操作過(guò)程中產(chǎn)生的不必要的 CPU 數(shù)據(jù)拷貝操作,但是,CPU 和存儲(chǔ)體系結(jié)構(gòu)的這種優(yōu)勢(shì)經(jīng)常被過(guò)高估計(jì)。存儲(chǔ)體系結(jié)構(gòu)的復(fù)雜性以及網(wǎng)絡(luò)協(xié)議中必需的數(shù)據(jù)傳輸可能會(huì)產(chǎn)生問(wèn)題,有時(shí)甚至?xí)?dǎo)致零拷貝這種技術(shù)的優(yōu)點(diǎn)完全喪失。在下一章中,我們會(huì)介紹幾種 Linux 操作系統(tǒng)中出現(xiàn)的零拷貝技術(shù),簡(jiǎn)單描述一下它們的實(shí)現(xiàn)方法,并對(duì)它們的弱點(diǎn)進(jìn)行分析。

零拷貝技術(shù)分類(lèi)

零拷貝技術(shù)的發(fā)展很多樣化,現(xiàn)有的零拷貝技術(shù)種類(lèi)也非常多,而當(dāng)前并沒(méi)有一個(gè)適合于所有場(chǎng)景的零拷貝技術(shù)的出現(xiàn)。對(duì)于 Linux 來(lái)說(shuō),現(xiàn)存的零拷貝技術(shù)也比較多,這些零拷貝技術(shù)大部分存在于不同的 Linux 內(nèi)核版本,有些舊的技術(shù)在不同的 Linux 內(nèi)核版本間得到了很大的發(fā)展或者已經(jīng)漸漸被新的技術(shù)所代替。本文針對(duì)這些零拷貝技術(shù)所適用的不同場(chǎng)景對(duì)它們進(jìn)行了劃分。概括起來(lái),Linux 中的零拷貝技術(shù)主要有下面這幾種:

  • 直接 I/O:對(duì)于這種數(shù)據(jù)傳輸方式來(lái)說(shuō),應(yīng)用程序可以直接訪(fǎng)問(wèn)硬件存儲(chǔ),操作系統(tǒng)內(nèi)核只是輔助數(shù)據(jù)傳輸:這類(lèi)零拷貝技術(shù)針對(duì)的是操作系統(tǒng)內(nèi)核并不需要對(duì)數(shù)據(jù)進(jìn)行直接處理的情況,數(shù)據(jù)可以在應(yīng)用程序地址空間的緩沖區(qū)和磁盤(pán)之間直接進(jìn)行傳輸,完全不需要 Linux 操作系統(tǒng)內(nèi)核提供的頁(yè)緩存的支持。
  • 在數(shù)據(jù)傳輸?shù)倪^(guò)程中,避免數(shù)據(jù)在操作系統(tǒng)內(nèi)核地址空間的緩沖區(qū)和用戶(hù)應(yīng)用程序地址空間的緩沖區(qū)之間進(jìn)行拷貝。有的時(shí)候,應(yīng)用程序在數(shù)據(jù)進(jìn)行傳輸?shù)倪^(guò)程中不需要對(duì)數(shù)據(jù)進(jìn)行訪(fǎng)問(wèn),那么,將數(shù)據(jù)從 Linux 的頁(yè)緩存拷貝到用戶(hù)進(jìn)程的緩沖區(qū)中就可以完全避免,傳輸?shù)臄?shù)據(jù)在頁(yè)緩存中就可以得到處理。在某些特殊的情況下,這種零拷貝技術(shù)可以獲得較好的性能。Linux 中提供類(lèi)似的系統(tǒng)調(diào)用主要有 mmap(),sendfile() 以及 splice()。
  • 對(duì)數(shù)據(jù)在 Linux 的頁(yè)緩存和用戶(hù)進(jìn)程的緩沖區(qū)之間的傳輸過(guò)程進(jìn)行優(yōu)化。該零拷貝技術(shù)側(cè)重于靈活地處理數(shù)據(jù)在用戶(hù)進(jìn)程的緩沖區(qū)和操作系統(tǒng)的頁(yè)緩存之間的拷貝操作。這種方法延續(xù)了傳統(tǒng)的通信方式,但是更加靈活。在Linux中,該方法主要利用了寫(xiě)時(shí)復(fù)制技術(shù)。

前兩類(lèi)方法的目的主要是為了避免應(yīng)用程序地址空間和操作系統(tǒng)內(nèi)核地址空間這兩者之間的緩沖區(qū)拷貝操作。這兩類(lèi)零拷貝技術(shù)通常適用在某些特殊的情況下,比如要傳送的數(shù)據(jù)不需要經(jīng)過(guò)操作系統(tǒng)內(nèi)核的處理或者不需要經(jīng)過(guò)應(yīng)用程序的處理。第三類(lèi)方法則繼承了傳統(tǒng)的應(yīng)用程序地址空間和操作系統(tǒng)內(nèi)核地址空間之間數(shù)據(jù)傳輸?shù)母拍?,進(jìn)而針對(duì)數(shù)據(jù)傳輸本身進(jìn)行優(yōu)化。我們知道,硬件和軟件之間的數(shù)據(jù)傳輸可以通過(guò)使用 DMA 來(lái)進(jìn)行,DMA  進(jìn)行數(shù)據(jù)傳輸?shù)倪^(guò)程中幾乎不需要  CPU  參與,這樣就可以把 CPU 解放出來(lái)去做更多其他的事情,但是當(dāng)數(shù)據(jù)需要在用戶(hù)地址空間的緩沖區(qū)和  Linux  操作系統(tǒng)內(nèi)核的頁(yè)緩存之間進(jìn)行傳輸?shù)臅r(shí)候,并沒(méi)有類(lèi)似  DMA  這種工具可以使用,CPU  需要全程參與到這種數(shù)據(jù)拷貝操作中,所以這第三類(lèi)方法的目的是可以有效地改善數(shù)據(jù)在用戶(hù)地址空間和操作系統(tǒng)內(nèi)核地址空間之間傳遞的效率。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容