《LINUX教程:Linux 中的零拷貝技術(shù)詳述》要點(diǎn):
本文介紹了LINUX教程:Linux 中的零拷貝技術(shù)詳述,希望對(duì)您有用。如果有疑問(wèn),可以聯(lián)系我們。
本系列由兩篇文章組成,介紹了當(dāng)前用于 Linux 操作系統(tǒng)上的幾種零拷貝技術(shù),簡(jiǎn)單描述了各種零拷貝技術(shù)的實(shí)現(xiàn),以及它們的特點(diǎn)和適用場(chǎng)景.本文是本系列文章的第一部分,主要是介紹一些零拷貝技術(shù)的相關(guān)配景知識(shí),簡(jiǎn)要概述了 Linux 為什么需要零拷貝技術(shù)以及 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)槿绻蟮臄?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)完全喪失.
如今,很多網(wǎng)絡(luò)服務(wù)器都是基于客戶(hù)端 - 服務(wù)器這一模型的.在這種模型中,客戶(hù)端向服務(wù)器端哀求數(shù)據(jù)或者服務(wù);服務(wù)器端則需要響應(yīng)客戶(hù)端發(fā)出的哀求,并為客戶(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)?舉個(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ā)送哀求,操作系統(tǒng)將這些客戶(hù)端的哀求傳遞給服務(wù)器端應(yīng)用程序,服務(wù)器端應(yīng)用程序會(huì)處理這些哀求,哀求處理完成以后,操作系統(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)的 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ù)男阅?
當(dāng)應(yīng)用程序需要訪問(wèn)某塊數(shù)據(jù)的時(shí)候,操作系統(tǒng)內(nèi)核會(huì)先檢查這塊數(shù)據(jù)是不是因?yàn)榍耙淮螌?duì)相同文件的訪問(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ù)存放到哀求這塊數(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 仍然需要拜訪數(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)和操作.
簡(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ù)拷貝和共享總線操作的次數(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ù)拷貝
將多種操作結(jié)合在一起
前文提到過(guò),對(duì)于高速網(wǎng)絡(luò)來(lái)說(shuō),零拷貝技術(shù)是非常重要的.這是因?yàn)楦咚倬W(wǎng)絡(luò)的網(wǎng)絡(luò)鏈接能力與 CPU 的處理能力接近,甚至?xí)^(guò) CPU 的處理能力.如果是這樣的話,那么 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ù)的發(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ù)主要有下面這幾種:
前兩類(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)核地址空間之間傳遞的效率.
本系列文章介紹了 Linux 中的零拷貝技術(shù),本文是其中的第一部分,介紹了零拷貝技術(shù)的基本概念,Linux 為什么需要零拷貝這種技術(shù)以及簡(jiǎn)要概述了 Linux 中都存在哪些零拷貝技術(shù)這樣一些基本配景知識(shí).我們將在本系列文章的第二部分內(nèi)容中詳細(xì)介紹本文提到的 Linux 中的幾種零拷貝技術(shù).
更多詳情見(jiàn)請(qǐng)繼續(xù)閱讀下一頁(yè)的出色內(nèi)容:
_baidu_page_break_tag_本系列由兩篇文章組成,介紹了當(dāng)前用于 Linux 操作系統(tǒng)上的幾種零拷貝技術(shù),簡(jiǎn)單描述了各種零拷貝技術(shù)的實(shí)現(xiàn),以及它們的特點(diǎn)和適用場(chǎng)景.第一部分主要介紹了一些零拷貝技術(shù)的相關(guān)配景知識(shí),簡(jiǎn)要概述了 Linux 為什么需要零拷貝技術(shù)以及 Linux 中都有哪幾種零拷貝技術(shù).本文是本系列文章的第二部分,針對(duì)第一部分內(nèi)容中提到的幾種零拷貝技術(shù)分別進(jìn)行更詳細(xì)的介紹,并對(duì)這些零拷貝技術(shù)的優(yōu)缺點(diǎn)進(jìn)行分析.
如果應(yīng)用程序可以直接拜訪網(wǎng)絡(luò)接口存儲(chǔ),那么在應(yīng)用程序拜訪數(shù)據(jù)之前存儲(chǔ)總線就不需要被遍歷,數(shù)據(jù)傳輸所引起的開(kāi)銷(xiāo)將會(huì)是最小的.應(yīng)用程序或者運(yùn)行在用戶(hù)模式下的庫(kù)函數(shù)可以直接拜訪硬件設(shè)備的存儲(chǔ),操作系統(tǒng)內(nèi)核除了進(jìn)行必要的虛擬存儲(chǔ)配置工作之外,不參與數(shù)據(jù)傳輸過(guò)程中的其它任何事情.直接 I/O 使得數(shù)據(jù)可以直接在應(yīng)用程序和外圍設(shè)備之間進(jìn)行傳輸,完全不需要操作系統(tǒng)內(nèi)核頁(yè)緩存的支持.關(guān)于直接 I/O 技術(shù)的具體實(shí)現(xiàn)細(xì)節(jié)可以參看 developerWorks 上的另一篇文章”Linux 中直接 I/O 機(jī)制的介紹” ,本文不做過(guò)多描述.
在 Linux 中,減少拷貝次數(shù)的一種辦法是調(diào)用 mmap() 來(lái)代替調(diào)用 read,比如:
tmp_buf = mmap(file, len); write(socket, tmp_buf, len);
首先,應(yīng)用程序調(diào)用了 mmap() 之后,數(shù)據(jù)會(huì)先通過(guò) DMA 拷貝到操作系統(tǒng)內(nèi)核的緩沖區(qū)中去.接著,應(yīng)用程序跟操作系統(tǒng)共享這個(gè)緩沖區(qū),這樣,操作系統(tǒng)內(nèi)核和應(yīng)用程序存儲(chǔ)空間就不需要再進(jìn)行任何的數(shù)據(jù)拷貝操作.應(yīng)用程序調(diào)用了 write() 之后,操作系統(tǒng)內(nèi)核將數(shù)據(jù)從本來(lái)的內(nèi)核緩沖區(qū)中拷貝到與 socket 相關(guān)的內(nèi)核緩沖區(qū)中.接下來(lái),數(shù)據(jù)從內(nèi)核 socket 緩沖區(qū)拷貝到協(xié)議引擎中去,這是第三次數(shù)據(jù)拷貝操作.
通過(guò)使用 mmap() 來(lái)代替 read(), 已經(jīng)可以減半操作系統(tǒng)需要進(jìn)行數(shù)據(jù)拷貝的次數(shù).當(dāng)大量數(shù)據(jù)需要傳輸?shù)臅r(shí)候,這樣做就會(huì)有一個(gè)比較好的效率.但是,這種改進(jìn)也是需要代價(jià)的,使用 mma()p 其實(shí)是存在潛在的問(wèn)題的.當(dāng)對(duì)文件進(jìn)行了內(nèi)存映射,然后調(diào)用 write() 系統(tǒng)調(diào)用,如果此時(shí)其他的進(jìn)程截?cái)嗔诉@個(gè)文件,那么 write() 系統(tǒng)調(diào)用將會(huì)被總線錯(cuò)誤信號(hào) SIGBUS 中斷,因?yàn)榇藭r(shí)正在執(zhí)行的是一個(gè)錯(cuò)誤的存儲(chǔ)拜訪.這個(gè)信號(hào)將會(huì)導(dǎo)致進(jìn)程被殺死,解決這個(gè)問(wèn)題可以通過(guò)以下這兩種方法:
使用 mmap 是 POSIX 兼容的,但是使用 mmap 并不一定能獲得理想的數(shù)據(jù)傳輸性能.數(shù)據(jù)傳輸?shù)倪^(guò)程中仍然需要一次 CPU 拷貝操作,而且映射操作也是一個(gè)開(kāi)銷(xiāo)很大的虛擬存儲(chǔ)操作,這種操作需要通過(guò)更改頁(yè)表以及沖刷 TLB (使得 TLB 的內(nèi)容無(wú)效)來(lái)維持存儲(chǔ)的一致性.但是,因?yàn)橛成渫ǔ_m用于較大范圍,所以對(duì)于相同長(zhǎng)度的數(shù)據(jù)來(lái)說(shuō),映射所帶來(lái)的開(kāi)銷(xiāo)遠(yuǎn)遠(yuǎn)低于 CPU 拷貝所帶來(lái)的開(kāi)銷(xiāo).
為了簡(jiǎn)化用戶(hù)接口,同時(shí)還要繼續(xù)保存 mmap()/write() 技術(shù)的優(yōu)點(diǎn):減少 CPU 的拷貝次數(shù),Linux 在版本 2.1 中引入了 sendfile() 這個(gè)系統(tǒng)調(diào)用.
sendfile() 不僅減少了數(shù)據(jù)拷貝操作,它也減少了上下文切換.首先:sendfile() 系統(tǒng)調(diào)用利用 DMA 引擎將文件中的數(shù)據(jù)拷貝到操作系統(tǒng)內(nèi)核緩沖區(qū)中,然后數(shù)據(jù)被拷貝到與 socket 相關(guān)的內(nèi)核緩沖區(qū)中去.接下來(lái),DMA 引擎將數(shù)據(jù)從內(nèi)核 socket 緩沖區(qū)中拷貝到協(xié)議引擎中去.如果在用戶(hù)調(diào)用 sendfile () 系統(tǒng)調(diào)用進(jìn)行數(shù)據(jù)傳輸?shù)倪^(guò)程中有其他進(jìn)程截?cái)嗔嗽撐募?那么 sendfile () 系統(tǒng)調(diào)用會(huì)簡(jiǎn)單地返回給用戶(hù)應(yīng)用法式中斷前所傳輸?shù)淖止?jié)數(shù),errno 會(huì)被設(shè)置為 success.如果在調(diào)用 sendfile() 之前操作系統(tǒng)對(duì)文件加上了租借鎖,那么 sendfile() 的操作和返回狀態(tài)將會(huì)和 mmap()/write () 一樣.
sendfile() 系統(tǒng)調(diào)用不需要將數(shù)據(jù)拷貝或者映射到應(yīng)用程序地址空間中去,所以 sendfile() 只是適用于應(yīng)用程序地址空間不需要對(duì)所拜訪數(shù)據(jù)進(jìn)行處理的情況.相對(duì)于 mmap() 方法來(lái)說(shuō),因?yàn)?sendfile 傳輸?shù)臄?shù)據(jù)沒(méi)有越過(guò)用戶(hù)應(yīng)用程序 / 操作系統(tǒng)內(nèi)核的邊界線,所以 sendfile () 也極大地減少了存儲(chǔ)管理的開(kāi)銷(xiāo).但是,sendfile () 也有很多局限性,如下所列:
上小節(jié)介紹的 sendfile() 技術(shù)在進(jìn)行數(shù)據(jù)傳輸仍然還需要一次多余的數(shù)據(jù)拷貝操作,通過(guò)引入一點(diǎn)硬件上的幫助,這僅有的一次數(shù)據(jù)拷貝操作也可以避免.為了避免操作系統(tǒng)內(nèi)核造成的數(shù)據(jù)副本,需要用到一個(gè)支持收集操作的網(wǎng)絡(luò)接口,這也就是說(shuō),待傳輸?shù)臄?shù)據(jù)可以分散在存儲(chǔ)的不同位置上,而不需要在連續(xù)存儲(chǔ)中存放.這樣一來(lái),從文件中讀出的數(shù)據(jù)就根本不需要被拷貝到 socket 緩沖區(qū)中去,而只是需要將緩沖區(qū)描述符傳到網(wǎng)絡(luò)協(xié)議棧中去,之后其在緩沖區(qū)中建立起數(shù)據(jù)包的相關(guān)結(jié)構(gòu),然后通過(guò) DMA 收集拷貝功能將所有的數(shù)據(jù)結(jié)合成一個(gè)網(wǎng)絡(luò)數(shù)據(jù)包.網(wǎng)卡的 DMA 引擎會(huì)在一次操作中從多個(gè)位置讀取包頭和數(shù)據(jù).Linux 2.4 版本中的 socket 緩沖區(qū)就可以滿(mǎn)足這種條件,這也就是用于 Linux 中的眾所周知的零拷貝技術(shù),這種辦法不但減少了因?yàn)槎啻紊舷挛那袚Q所帶來(lái)開(kāi)銷(xiāo),同時(shí)也減少了處理器造成的數(shù)據(jù)副本的個(gè)數(shù).對(duì)于用戶(hù)應(yīng)用程序來(lái)說(shuō),代碼沒(méi)有任何改變.首先,sendfile() 系統(tǒng)調(diào)用利用 DMA 引擎將文件內(nèi)容拷貝到內(nèi)核緩沖區(qū)去;然后,將帶有文件位置和長(zhǎng)度信息的緩沖區(qū)描述符添加到 socket 緩沖區(qū)中去,此過(guò)程不需要將數(shù)據(jù)從操作系統(tǒng)內(nèi)核緩沖區(qū)拷貝到 socket 緩沖區(qū)中,DMA 引擎會(huì)將數(shù)據(jù)直接從內(nèi)核緩沖區(qū)拷貝到協(xié)議引擎中去,這樣就避免了最后一次數(shù)據(jù)拷貝.
通過(guò)這種方法,CPU 在數(shù)據(jù)傳輸?shù)倪^(guò)程中不但避免了數(shù)據(jù)拷貝操作,理論上,CPU 也永遠(yuǎn)不會(huì)跟傳輸?shù)臄?shù)據(jù)有任何關(guān)聯(lián),這對(duì)于 CPU 的性能來(lái)說(shuō)起到了積極的作用:首先,高速緩沖存儲(chǔ)器沒(méi)有受到污染;其次,高速緩沖存儲(chǔ)器的一致性不需要維護(hù),高速緩沖存儲(chǔ)器在 DMA 進(jìn)行數(shù)據(jù)傳輸前或者傳輸后不需要被刷新.然而實(shí)際上,后者實(shí)現(xiàn)起來(lái)非常困難.源緩沖區(qū)有可能是頁(yè)緩存的一部分,這也就是說(shuō)一般的讀操作可以拜訪它,而且該拜訪也可以是通過(guò)傳統(tǒng)方式進(jìn)行的.只要存儲(chǔ)區(qū)域可以被 CPU 拜訪到,那么高速緩沖存儲(chǔ)器的一致性就需要通過(guò) DMA 傳輸之前沖刷新高速緩沖存儲(chǔ)器來(lái)維護(hù).而且,這種數(shù)據(jù)收集拷貝功能的實(shí)現(xiàn)是需要硬件以及設(shè)備驅(qū)動(dòng)程序支持的.
splice() 是 Linux 中與 mmap() 和 sendfile() 類(lèi)似的一種辦法.它也可以用于用戶(hù)應(yīng)用程序地址空間和操作系統(tǒng)地址空間之間的數(shù)據(jù)傳輸.splice() 適用于可以確定數(shù)據(jù)傳輸路徑的用戶(hù)應(yīng)用程序,它不需要利用用戶(hù)地址空間的緩沖區(qū)進(jìn)行顯式的數(shù)據(jù)傳輸操作.那么,當(dāng)數(shù)據(jù)只是從一個(gè)地方傳送到另一個(gè)地方,過(guò)程中所傳輸?shù)臄?shù)據(jù)不需要經(jīng)過(guò)用戶(hù)應(yīng)用程序的處理的時(shí)候,spice() 就成為了一種比較好的選擇.splice() 可以在操作系統(tǒng)地址空間中整塊地移動(dòng)數(shù)據(jù),從而減少大多數(shù)數(shù)據(jù)拷貝操作.而且,splice() 進(jìn)行數(shù)據(jù)傳輸可以通過(guò)異步的方式來(lái)進(jìn)行,用戶(hù)應(yīng)用程序可以先從系統(tǒng)調(diào)用返回,而操作系統(tǒng)內(nèi)核進(jìn)程會(huì)控制數(shù)據(jù)傳輸過(guò)程繼續(xù)進(jìn)行下去.splice() 可以被看成是類(lèi)似于基于流的管道的實(shí)現(xiàn),管道可以使得兩個(gè)文件描述符相互連接,splice 的調(diào)用者則可以控制兩個(gè)設(shè)備(或者協(xié)議棧)在操作系統(tǒng)內(nèi)核中的相互連接.
splice() 系統(tǒng)調(diào)用和 sendfile() 非常類(lèi)似,用戶(hù)應(yīng)用程序必需擁有兩個(gè)已經(jīng)打開(kāi)的文件描述符,一個(gè)用于表示輸入設(shè)備,一個(gè)用于表示輸出設(shè)備.與 sendfile() 不同的是,splice() 允許任意兩個(gè)文件之間互相連接,而并不只是文件到 socket 進(jìn)行數(shù)據(jù)傳輸.對(duì)于從一個(gè)文件描述符發(fā)送數(shù)據(jù)到 socket 這種特例來(lái)說(shuō),一直都是使用 sendfile() 這個(gè)系統(tǒng)調(diào)用,而 splice 一直以來(lái)就只是一種機(jī)制,它并不僅限于 sendfile() 的功能.也就是說(shuō),sendfile() 只是 splice() 的一個(gè)子集,在 Linux 2.6.23 中,sendfile() 這種機(jī)制的實(shí)現(xiàn)已經(jīng)沒(méi)有了,但是這個(gè) API 以及相應(yīng)的功能還存在,只不過(guò) API 以及相應(yīng)的功能是利用了 splice() 這種機(jī)制來(lái)實(shí)現(xiàn)的.
在數(shù)據(jù)傳輸?shù)倪^(guò)程中,splice() 機(jī)制交替地發(fā)送相關(guān)的文件描述符的讀寫(xiě)操作,并且可以將讀緩沖區(qū)重新用于寫(xiě)操作.它也利用了一種簡(jiǎn)單的流控制,通過(guò)預(yù)先定義的水印( watermark )來(lái)阻塞寫(xiě)哀求.有實(shí)驗(yàn)表明,利用這種方法將數(shù)據(jù)從一個(gè)磁盤(pán)傳輸?shù)搅硪粋€(gè)磁盤(pán)會(huì)增加 30% 到 70% 的吞吐量,數(shù)據(jù)傳輸?shù)倪^(guò)程中, CPU 的負(fù)載也會(huì)減少一半.
Linux 2.6.17 內(nèi)核引入了 splice() 系統(tǒng)調(diào)用,但是,這個(gè)概念在此之前 ] 其實(shí)已經(jīng)存在了很長(zhǎng)一段時(shí)間了.1988 年,Larry McVoy 提出了這個(gè)概念,它被看成是一種改進(jìn)服務(wù)器端系統(tǒng)的 I/O 性能的一種技術(shù),盡管在之后的若干年中經(jīng)常被提及,但是 splice 系統(tǒng)調(diào)用從來(lái)沒(méi)有在主流的 Linux 操作系統(tǒng)內(nèi)核中實(shí)現(xiàn)過(guò),一直到 Linux 2.6.17 版本的出現(xiàn).splice 系統(tǒng)調(diào)用必要用到四個(gè)參數(shù),其中兩個(gè)是文件描述符,一個(gè)表示文件長(zhǎng)度,還有一個(gè)用于控制如何進(jìn)行數(shù)據(jù)拷貝.splice 系統(tǒng)調(diào)用可以同步實(shí)現(xiàn),也可以使用異步方式來(lái)實(shí)現(xiàn).在使用異步方式的時(shí)候,用戶(hù)應(yīng)用程序會(huì)通過(guò)信號(hào) SIGIO 來(lái)獲知數(shù)據(jù)傳輸已經(jīng)終止.splice() 系統(tǒng)調(diào)用的接口如下所示:
long splice(int fdin, int fdout, size_t len, unsigned int flags);
調(diào)用 splice() 系統(tǒng)調(diào)用會(huì)導(dǎo)致操作系統(tǒng)內(nèi)核從數(shù)據(jù)源 fdin 移動(dòng)最多 len 個(gè)字節(jié)的數(shù)據(jù)到 fdout 中去,這個(gè)數(shù)據(jù)的移動(dòng)過(guò)程只是經(jīng)過(guò)操作系統(tǒng)內(nèi)核空間,需要最少的拷貝次數(shù).使用 splice() 系統(tǒng)調(diào)用需要這兩個(gè)文件描述符中的一個(gè)必須是用來(lái)表示一個(gè)管道設(shè)備的.不難看出,這種設(shè)計(jì)具有局限性,Linux 的后續(xù)版本針對(duì)這一問(wèn)題將會(huì)有所改進(jìn).參數(shù) flags 用于表示拷貝操作的執(zhí)行辦法,當(dāng)前的 flags 有如下這些取值:
Splice() 系統(tǒng)調(diào)用利用了 Linux 提出的管道緩沖區(qū)( pipe buffer )機(jī)制,這就是為什么這個(gè)系統(tǒng)調(diào)用的兩個(gè)文件描述符參數(shù)中至少有一個(gè)必需要指代管道設(shè)備的原因.為了支持 splice 這種機(jī)制,Linux 在用于設(shè)備和文件系統(tǒng)的 file_operations 結(jié)構(gòu)中增加了下邊這兩個(gè)定義:
ssize_t (*splice_write)(struct inode *pipe, strucuct file *out, size_t len, unsigned int flags); ssize_t (*splice_read)(struct inode *in, strucuct file *pipe, size_t len, unsigned int flags);
這兩個(gè)新的操作可以根據(jù) flags 的設(shè)定在 pipe 和 in 或者 out 之間移動(dòng) len 個(gè)字節(jié).Linux 文件系統(tǒng)已經(jīng)實(shí)現(xiàn)了具有上述功能而且可以使用的操作,而且還實(shí)現(xiàn)了一個(gè) generic_splice_sendpage() 函數(shù)用于和 socket 之間的接合.
前面提到的幾種零拷貝技術(shù)都是通過(guò)盡量避免用戶(hù)應(yīng)用程序和操作系統(tǒng)內(nèi)核緩沖區(qū)之間的數(shù)據(jù)拷貝來(lái)實(shí)現(xiàn)的,使用上面那些零拷貝技術(shù)的應(yīng)用程序通常都要局限于某些特殊的情況:要么不能在操作系統(tǒng)內(nèi)核中處理數(shù)據(jù),要么不能在用戶(hù)地址空間中處理數(shù)據(jù).而這一小節(jié)提出的零拷貝技術(shù)保存了傳統(tǒng)在用戶(hù)應(yīng)用程序地址空間和操作系統(tǒng)內(nèi)核地址空間之間傳遞數(shù)據(jù)的技術(shù),但卻在傳輸上進(jìn)行優(yōu)化.我們知道,數(shù)據(jù)在系統(tǒng)軟件和硬件之間的傳遞可以通過(guò) DMA 傳輸來(lái)提高效率,但是對(duì)于用戶(hù)應(yīng)用程序和操作系統(tǒng)之間進(jìn)行數(shù)據(jù)傳輸這種情況來(lái)說(shuō),并沒(méi)有類(lèi)似的工具可以使用.本節(jié)介紹的技術(shù)就是針對(duì)這種情況提出來(lái)的.
在某些情況下,Linux 操作系統(tǒng)內(nèi)核中的頁(yè)緩存可能會(huì)被多個(gè)應(yīng)用程序所共享,操作系統(tǒng)有可能會(huì)將用戶(hù)應(yīng)用程序地址空間緩沖區(qū)中的頁(yè)面映射到操作系統(tǒng)內(nèi)核地址空間中去.如果某個(gè)應(yīng)用程序想要對(duì)這共享的數(shù)據(jù)調(diào)用 write() 系統(tǒng)調(diào)用,那么它就可能破壞內(nèi)核緩沖區(qū)中的共享數(shù)據(jù),傳統(tǒng)的 write() 系統(tǒng)調(diào)用并沒(méi)有提供任何顯示的加鎖操作,Linux 中引入了寫(xiě)時(shí)復(fù)制這樣一種技術(shù)用來(lái)掩護(hù)數(shù)據(jù).
什么是寫(xiě)時(shí)復(fù)制
寫(xiě)時(shí)復(fù)制是計(jì)算機(jī)編程中的一種優(yōu)化策略,它的基本思想是這樣的:如果有多個(gè)應(yīng)用程序需要同時(shí)拜訪同一塊數(shù)據(jù),那么可以為這些應(yīng)用程序分配指向這塊數(shù)據(jù)的指針,在每一個(gè)應(yīng)用程序看來(lái),它們都擁有這塊數(shù)據(jù)的一份數(shù)據(jù)拷貝,當(dāng)其中一個(gè)應(yīng)用程序需要對(duì)自己的這份數(shù)據(jù)拷貝進(jìn)行修改的時(shí)候,就需要將數(shù)據(jù)真正地拷貝到該應(yīng)用程序的地址空間中去,也就是說(shuō),該應(yīng)用程序擁有了一份真正的私有數(shù)據(jù)拷貝,這樣做是為了避免該應(yīng)用程序?qū)@塊數(shù)據(jù)做的更改被其他應(yīng)用程序看到.這個(gè)過(guò)程對(duì)于應(yīng)用程序來(lái)說(shuō)是透明的,如果應(yīng)用程序永遠(yuǎn)不會(huì)對(duì)所拜訪的這塊數(shù)據(jù)進(jìn)行任何更改,那么就永遠(yuǎn)不需要將數(shù)據(jù)拷貝到應(yīng)用程序自己的地址空間中去.這也是寫(xiě)時(shí)復(fù)制的最主要的優(yōu)點(diǎn).
寫(xiě)時(shí)復(fù)制的實(shí)現(xiàn)必要 MMU 的支持,MMU 必要知曉進(jìn)程地址空間中哪些特殊的頁(yè)面是只讀的,當(dāng)必要往這些頁(yè)面中寫(xiě)數(shù)據(jù)的時(shí)候,MMU 就會(huì)發(fā)出一個(gè)異常給操作系統(tǒng)內(nèi)核,操作系統(tǒng)內(nèi)核就會(huì)分配新的物理存儲(chǔ)空間,即將被寫(xiě)入數(shù)據(jù)的頁(yè)面必要與新的物理存儲(chǔ)位置相對(duì)應(yīng).
寫(xiě)時(shí)復(fù)制的最大好處便是可以節(jié)約內(nèi)存.不過(guò)對(duì)于操作系統(tǒng)內(nèi)核來(lái)說(shuō),寫(xiě)時(shí)復(fù)制增加了其處理過(guò)程的復(fù)雜性.
數(shù)據(jù)傳輸?shù)膶?shí)現(xiàn)及其局限性
數(shù)據(jù)發(fā)送端
對(duì)于數(shù)據(jù)傳輸?shù)陌l(fā)送端來(lái)說(shuō),實(shí)現(xiàn)相對(duì)來(lái)說(shuō)是比較簡(jiǎn)單的,對(duì)與應(yīng)用程序緩沖區(qū)相關(guān)的物理頁(yè)面進(jìn)行加鎖,并將這些頁(yè)面映射到操作系統(tǒng)內(nèi)核的地址空間,并標(biāo)識(shí)為“ write only ”.當(dāng)系統(tǒng)調(diào)用返回的時(shí)候,用戶(hù)應(yīng)用程序和網(wǎng)絡(luò)堆棧就都可以讀取該緩沖區(qū)中的數(shù)據(jù).在操作系統(tǒng)已經(jīng)傳送完所有的數(shù)據(jù)之后,應(yīng)用程序就可以對(duì)這些數(shù)據(jù)進(jìn)行寫(xiě)操作.如果應(yīng)用程序嘗試在數(shù)據(jù)傳輸完成之前對(duì)數(shù)據(jù)進(jìn)行寫(xiě)操作,那么就會(huì)產(chǎn)生異常,這個(gè)時(shí)候操作系統(tǒng)就會(huì)將數(shù)據(jù)拷貝到應(yīng)用程序本身的緩沖區(qū)中去,并且重置應(yīng)用程序端的映射.數(shù)據(jù)傳輸完成之后,對(duì)加鎖的頁(yè)面進(jìn)行解鎖操作,并重置 COW 標(biāo)識(shí).
數(shù)據(jù)接管端
對(duì)于數(shù)據(jù)接收端來(lái)說(shuō),該技術(shù)的實(shí)現(xiàn)則需要處理復(fù)雜得多的情況.如果 read() 系統(tǒng)調(diào)用是在數(shù)據(jù)包到達(dá)之前發(fā)出的,并且應(yīng)用程序是被阻塞的,那么 read() 系統(tǒng)調(diào)用就會(huì)告知操作系統(tǒng)接收到的數(shù)據(jù)包中的數(shù)據(jù)應(yīng)該存放到什么地方去.在這種情況下,根本沒(méi)有必要進(jìn)行頁(yè)面重映射,網(wǎng)絡(luò)接口卡可以提供足夠的支持讓數(shù)據(jù)直接存入用戶(hù)應(yīng)用程序的緩沖區(qū)中去.如果數(shù)據(jù)接收是異步的,在 read() 系統(tǒng)調(diào)用發(fā)出之前,操作系統(tǒng)不知道該把數(shù)據(jù)寫(xiě)到哪里,因?yàn)樗恢烙脩?hù)應(yīng)用程序緩沖區(qū)的位置,所以操作系統(tǒng)內(nèi)核必須要先把數(shù)據(jù)存放到本身的緩沖區(qū)中去.
局限性
寫(xiě)時(shí)復(fù)制技術(shù)有可能會(huì)導(dǎo)致操作系統(tǒng)的處理開(kāi)銷(xiāo)很大.所有相關(guān)的緩沖區(qū)都必須要進(jìn)行頁(yè)對(duì)齊處理,并且使用的 MMU 頁(yè)面一定要是整數(shù)個(gè)的.對(duì)于發(fā)送端來(lái)說(shuō),這不會(huì)造成什么問(wèn)題.但是對(duì)于接收端來(lái)說(shuō),它需要有能力處理更加復(fù)雜的情況.首先,數(shù)據(jù)包的尺寸大小要合適,大小需要恰到好處能夠覆蓋一整頁(yè)的數(shù)據(jù),這就限制了那些 MTU 大小大于系統(tǒng)內(nèi)存頁(yè)的網(wǎng)絡(luò),比如 FDDI 和 ATM.其次,為了在沒(méi)有任何中斷的情況下將頁(yè)面重映射到數(shù)據(jù)包的流,數(shù)據(jù)包中的數(shù)據(jù)部分必須占用整數(shù)個(gè)頁(yè)面.對(duì)于異步接收數(shù)據(jù)的情況來(lái)說(shuō),為了將數(shù)據(jù)高效地移動(dòng)到用戶(hù)地址空間中去,可以使用這樣一種辦法:利用網(wǎng)絡(luò)接口卡的支持,傳來(lái)的數(shù)據(jù)包可以被分割成包頭和數(shù)據(jù)兩部分,數(shù)據(jù)被存放在一個(gè)單獨(dú)的緩沖區(qū)內(nèi),虛擬存儲(chǔ)系統(tǒng)然后就會(huì)將數(shù)據(jù)映射到用戶(hù)地址空間緩沖區(qū)去.使用這種辦法需要滿(mǎn)足兩個(gè)先決條件,也就是上面提到過(guò)的:一是應(yīng)用程序緩沖區(qū)必須是頁(yè)對(duì)齊的,并且在虛擬存儲(chǔ)上是連續(xù)的;二是傳來(lái)的數(shù)據(jù)有一頁(yè)大小的時(shí)候才可以對(duì)數(shù)據(jù)包進(jìn)行分割.事實(shí)上,這兩個(gè)先決條件是很難滿(mǎn)足的.如果應(yīng)用程序緩沖區(qū)不是頁(yè)對(duì)齊的,或者數(shù)據(jù)包的大小超過(guò)一個(gè)頁(yè),那么數(shù)據(jù)就需要被拷貝.對(duì)于數(shù)據(jù)發(fā)送端來(lái)說(shuō),就算數(shù)據(jù)在傳輸?shù)倪^(guò)程中對(duì)于應(yīng)用程序來(lái)說(shuō)是寫(xiě)保護(hù)的,應(yīng)用程序仍然需要避免使用這些忙緩沖區(qū),這是因?yàn)閷?xiě)時(shí)拷貝操作所帶來(lái)的開(kāi)銷(xiāo)是很大的.如果沒(méi)有端到端這一級(jí)別的通知,那么應(yīng)用程序很難會(huì)知道某緩沖區(qū)是否已經(jīng)被釋放還是仍然在被占用.
這種零拷貝技術(shù)比較適用于那種寫(xiě)時(shí)復(fù)制事件發(fā)生比較少的情況,因?yàn)閷?xiě)時(shí)復(fù)制事件所產(chǎn)生的開(kāi)銷(xiāo)要遠(yuǎn)遠(yuǎn)高于一次 CPU 拷貝所產(chǎn)生的開(kāi)銷(xiāo).實(shí)際情況中,大多數(shù)應(yīng)用程序通常都會(huì)多次重復(fù)使用相同的緩沖區(qū),所以,一次使用完數(shù)據(jù)之后,不要從操作系統(tǒng)地址空間解除頁(yè)面的映射,這樣會(huì)提高效率.考慮到同樣的頁(yè)面可能會(huì)被再次拜訪,所以保留頁(yè)面的映射可以節(jié)省管理開(kāi)銷(xiāo),但是,這種映射保留不會(huì)減少由于頁(yè)表往返移動(dòng)和 TLB 沖刷所帶來(lái)的開(kāi)銷(xiāo),這是因?yàn)槊看雾?yè)面由于寫(xiě)時(shí)復(fù)制而進(jìn)行加鎖或者解鎖的時(shí)候,頁(yè)面的只讀標(biāo)志都要被更改.
還有另外一種利用預(yù)先映射機(jī)制的共享緩沖區(qū)的方法也可以在應(yīng)用程序地址空間和操作系統(tǒng)內(nèi)核之間快速傳輸數(shù)據(jù).采用緩沖區(qū)共享這種思想的架構(gòu)最先在 Solaris 上實(shí)現(xiàn),該架構(gòu)使用了“ fbufs ”這個(gè)概念.這種方法需要修改 API.應(yīng)用程序地址空間和操作系統(tǒng)內(nèi)核地址空間之間的數(shù)據(jù)傳遞需要嚴(yán)格依照 fbufs 體系結(jié)構(gòu)來(lái)實(shí)現(xiàn),操作系統(tǒng)內(nèi)核之間的通信也是嚴(yán)格依照 fbufs 體系結(jié)構(gòu)來(lái)完成的.每一個(gè)應(yīng)用程序都有一個(gè)緩沖區(qū)池,這個(gè)緩沖區(qū)池被同時(shí)映射到用戶(hù)地址空間和內(nèi)核地址空間,也可以在必要的時(shí)候才創(chuàng)建它們.通過(guò)完成一次虛擬存儲(chǔ)操作來(lái)創(chuàng)建緩沖區(qū),fbufs 可以有效地減少由存儲(chǔ)一致性維護(hù)所引起的大多數(shù)性能問(wèn)題.該技術(shù)在 Linux 中還停留在實(shí)驗(yàn)階段.
為什么要擴(kuò)大 Linux I/O API
傳統(tǒng)的 Linux 輸入輸出接口,好比讀和寫(xiě)系統(tǒng)調(diào)用,都是基于拷貝的,也就是說(shuō),數(shù)據(jù)需要在操作系統(tǒng)內(nèi)核和應(yīng)用程序定義的緩沖區(qū)之間進(jìn)行拷貝.對(duì)于讀系統(tǒng)調(diào)用來(lái)說(shuō),用戶(hù)應(yīng)用程序呈現(xiàn)給操作系統(tǒng)內(nèi)核一個(gè)預(yù)先分配好的緩沖區(qū),內(nèi)核必須把讀進(jìn)來(lái)的數(shù)據(jù)放到這個(gè)緩沖區(qū)內(nèi).對(duì)于寫(xiě)系統(tǒng)調(diào)用來(lái)說(shuō),只要系統(tǒng)調(diào)用返回,用戶(hù)應(yīng)用程序就可以自由重新利用數(shù)據(jù)緩沖區(qū).
為了支持上面這種機(jī)制,Linux 需要能夠?yàn)槊恳粋€(gè)操作都進(jìn)行建立和刪除虛擬存儲(chǔ)映射.這種頁(yè)面重映射的機(jī)制依賴(lài)于機(jī)器配置、cache 體系結(jié)構(gòu)、TLB 未命中處理所帶來(lái)的開(kāi)銷(xiāo)以及處理器是單處理器還是多處理器等多種因素.如果能夠避免處理 I/O 哀求的時(shí)候虛擬存儲(chǔ) / TLB 操作所產(chǎn)生的開(kāi)銷(xiāo),則會(huì)極大地提高 I/O 的性能.fbufs 就是這樣一種機(jī)制.使用 fbufs 體系結(jié)構(gòu)就可以避免虛擬存儲(chǔ)操作.由數(shù)據(jù)顯示,fbufs 這種結(jié)構(gòu)在 DECStation? 5000/200 這個(gè)單處理器工作站上會(huì)取得比上面提到的頁(yè)面重映射方法好得多的性能.如果要使用 fbufs 這種體系結(jié)構(gòu),必須要擴(kuò)展 Linux API,從而實(shí)現(xiàn)一種有效而且全面的零拷貝技術(shù).
快速緩沖區(qū)( Fast Buffers )原理先容
I/O 數(shù)據(jù)存放在一些被稱(chēng)作 fbufs 的緩沖區(qū)內(nèi),每一個(gè)這樣的緩沖區(qū)都包含一個(gè)或者多個(gè)連續(xù)的虛擬存儲(chǔ)頁(yè).應(yīng)用程序拜訪 fbuf 是通過(guò)保護(hù)域來(lái)實(shí)現(xiàn)的,有如下這兩種方式:
對(duì)于第一種情況來(lái)說(shuō),這個(gè)保護(hù)域被稱(chēng)作是 fbuf 的“ originator ”;對(duì)于后一種情況來(lái)說(shuō),這個(gè)保護(hù)域被稱(chēng)作是 fbuf 的“ receiver ”.
傳統(tǒng)的 Linux I/O 接口支持?jǐn)?shù)據(jù)在應(yīng)用程序地址空間和操作系統(tǒng)內(nèi)核之間交換,這種交換操作導(dǎo)致所有的數(shù)據(jù)都需要進(jìn)行拷貝.如果采用 fbufs 這種辦法,需要交換的是包含數(shù)據(jù)的緩沖區(qū),這樣就消除了多余的拷貝操作.應(yīng)用程序?qū)?fbuf 傳遞給操作系統(tǒng)內(nèi)核,這樣就能減少傳統(tǒng)的 write 系統(tǒng)調(diào)用所產(chǎn)生的數(shù)據(jù)拷貝開(kāi)銷(xiāo).同樣的,應(yīng)用程序通過(guò) fbuf 來(lái)接收數(shù)據(jù),這樣也可以減少傳統(tǒng) read 系統(tǒng)調(diào)用所產(chǎn)生的數(shù)據(jù)拷貝開(kāi)銷(xiāo).如下圖所示:
I/O 子系統(tǒng)或者應(yīng)用法式都可以通過(guò) fbufs 管理器來(lái)分配 fbufs.一旦分配了 fbufs,這些 fbufs 就可以從法式傳遞到 I/O 子系統(tǒng),或者從 I/O 子系統(tǒng)傳遞到法式.使用完后,這些 fbufs 會(huì)被釋放回 fbufs 緩沖區(qū)池.
fbufs 在實(shí)現(xiàn)上有如下這些特征,如圖 9 所示:
前面提到,這種辦法需要修改 API,如果要使用 fbufs 體系結(jié)構(gòu),??用程序和 Linux 操作系統(tǒng)內(nèi)核驅(qū)動(dòng)程序都需要使用新的 API,如果應(yīng)用程序要發(fā)送數(shù)據(jù),那么它就要從緩沖區(qū)池里獲取一個(gè) fbuf,將數(shù)據(jù)填充進(jìn)去,然后通過(guò)文件描述符將數(shù)據(jù)發(fā)送出去.接收到的 fbufs 可以被應(yīng)用程序保留一段時(shí)間,之后,應(yīng)用程序可以使用它繼續(xù)發(fā)送其他的數(shù)據(jù),或者還給緩沖區(qū)池.但是,在某些情況下,需要對(duì)數(shù)據(jù)包內(nèi)的數(shù)據(jù)進(jìn)行重新組裝,那么通過(guò) fbuf 接收到數(shù)據(jù)的應(yīng)用程序就需要將數(shù)據(jù)拷貝到另外一個(gè)緩沖區(qū)內(nèi).再者,應(yīng)用程序不能對(duì)當(dāng)前正在被內(nèi)核處理的數(shù)據(jù)進(jìn)行修改,基于這一點(diǎn),fbufs 體系結(jié)構(gòu)引入了強(qiáng)制鎖的概念以保證其實(shí)現(xiàn).對(duì)于應(yīng)用程序來(lái)說(shuō),如果 fbufs 已經(jīng)被發(fā)送給操作系統(tǒng)內(nèi)核,那么應(yīng)用程序就不會(huì)再處理這些 fbufs.
fbufs 存在的一些問(wèn)題
管理共享緩沖區(qū)池需要應(yīng)用程序、網(wǎng)絡(luò)軟件、以及設(shè)備驅(qū)動(dòng)程序之間的緊密合作.對(duì)于數(shù)據(jù)接收端來(lái)說(shuō),網(wǎng)絡(luò)硬件必須要能夠?qū)⒌竭_(dá)的數(shù)據(jù)包利用 DMA 傳輸?shù)接山邮斩朔峙涞恼_的存儲(chǔ)緩沖區(qū)池中去.而且,應(yīng)用程序稍微不注意就會(huì)更改之前發(fā)到共享存儲(chǔ)中的數(shù)據(jù)的內(nèi)容,從而導(dǎo)致數(shù)據(jù)被破壞,但是這種問(wèn)題在應(yīng)用程序端是很難調(diào)試的.同時(shí),共享存儲(chǔ)這種模型很難與其他類(lèi)型的存儲(chǔ)對(duì)象關(guān)聯(lián)使用,但是應(yīng)用程序、網(wǎng)絡(luò)軟件以及設(shè)備驅(qū)動(dòng)程序之間的緊密合作是需要其他存儲(chǔ)管理器的支持的.對(duì)于共享緩沖區(qū)這種技術(shù)來(lái)說(shuō),雖然這種技術(shù)看起來(lái)前景光明,但是這種技術(shù)不但需要對(duì) API 進(jìn)行更改,而且需要對(duì)驅(qū)動(dòng)程序也進(jìn)行更改,并且這種技術(shù)本身也存在一些未辦理的問(wèn)題,這就使得這種技術(shù)目前還只是出于試驗(yàn)階段.在測(cè)試系統(tǒng)中,這種技術(shù)在性能上有很大的改進(jìn),不過(guò)這種新的架構(gòu)的整體安裝目前看起來(lái)還是不可行的.這種預(yù)先分配共享緩沖區(qū)的機(jī)制有時(shí)也因?yàn)榱6葐?wèn)題需要將數(shù)據(jù)拷貝到另外一個(gè)緩沖區(qū)中去.
本系列文章介紹了 Linux 中的零拷貝技術(shù),本文是其中的第二部分.本文對(duì)第一部分文章中提出的 Linux 操作系統(tǒng)上出現(xiàn)的幾種零拷貝技術(shù)進(jìn)行了更詳細(xì)的介紹,主要描述了它們各自的優(yōu)點(diǎn),缺點(diǎn)以及適用場(chǎng)景.對(duì)于網(wǎng)絡(luò)數(shù)據(jù)傳輸來(lái)說(shuō),零拷貝技術(shù)的應(yīng)用受到了很多體系結(jié)構(gòu)方面因素的阻礙,包括虛擬存儲(chǔ)體系結(jié)構(gòu)以及網(wǎng)絡(luò)協(xié)議體系結(jié)構(gòu)等.所以,零拷貝技術(shù)仍然只是在某些很特殊的情況中才可以應(yīng)用,比如文件服務(wù)或者使用某種特殊的協(xié)議進(jìn)行高帶寬的通信等.但是,零拷貝技術(shù)在磁盤(pán)操作中的應(yīng)用的可行性就高得多了,這很可能是因?yàn)榇疟P(pán)操作具有同步的特點(diǎn),以及數(shù)據(jù)傳輸單元是依照頁(yè)的粒度來(lái)進(jìn)行的.
針對(duì) Linux 操作系統(tǒng)平臺(tái)提出并實(shí)現(xiàn)了很多種零拷貝技術(shù),但是并不是所有這些零拷貝技術(shù)都被廣泛應(yīng)用于現(xiàn)實(shí)中的操作系統(tǒng)中的.比如,fbufs 體系結(jié)構(gòu),它在很多方面看起來(lái)都很吸引人,但是使用它需要更改 API 以及驅(qū)動(dòng)程序,它還存在其他一些實(shí)現(xiàn)上的困難,這就使得 fbufs 還只是停留在實(shí)驗(yàn)的階段.動(dòng)態(tài)地址重映射技術(shù)只是需要對(duì)操作系統(tǒng)做少量修改,雖然不需要修改用戶(hù)軟件,但是當(dāng)前的虛擬存儲(chǔ)體系結(jié)構(gòu)并不能很好地支持頻繁的虛擬地址重映射操作.而且為了保證存儲(chǔ)的一致性,重映射之后還必須對(duì) TLB 和一級(jí)緩存進(jìn)行刷新.事實(shí)上,利用地址重映射實(shí)現(xiàn)的零拷貝技術(shù)適用的范圍是很小的,這是因?yàn)樘摂M存儲(chǔ)操作所帶來(lái)的開(kāi)銷(xiāo)往往要比 CPU 拷貝所產(chǎn)生的開(kāi)銷(xiāo)還要大.此外,為了完全消除 CPU 拜訪存儲(chǔ),通常都需要額外的硬件來(lái)支持,而這種硬件的支持并不是很普及,同時(shí)也是非常昂貴的.
本系列文章的目的是想贊助讀者理清這些出現(xiàn)在 Linux 操作系統(tǒng)中的零拷貝技術(shù)都是從何種角度來(lái)贊助改善數(shù)據(jù)傳輸過(guò)程中遇到的性能問(wèn)題的.關(guān)于各種零拷貝技術(shù)的具體實(shí)現(xiàn)細(xì)節(jié),本系列文章沒(méi)有做詳細(xì)描述.同時(shí),零拷貝技術(shù)一直是在不斷地發(fā)展和完善當(dāng)中的,本系列文章并沒(méi)有涵蓋 Linux 上出現(xiàn)的所有零拷貝技術(shù).
本文永遠(yuǎn)更新鏈接地址:
《LINUX教程:Linux 中的零拷貝技術(shù)詳述》是否對(duì)您有啟發(fā),歡迎查看更多與《LINUX教程:Linux 中的零拷貝技術(shù)詳述》相關(guān)教程,學(xué)精學(xué)透。維易PHP學(xué)院為您提供精彩教程。
轉(zhuǎn)載請(qǐng)注明本頁(yè)網(wǎng)址:
http://www.fzlkiss.com/jiaocheng/12104.html