Java中的零拷貝原理

[TOC]

先提出兩個(gè)問題:
IO過程中,哪些步驟進(jìn)行了拷貝充甚?哪些地方零拷貝爬立?
Java支持哪些零拷貝?

哪里聽說過零拷貝?真的0次拷貝嗎?

零拷貝(英語: Zero-copy) 技術(shù)是指計(jì)算機(jī)執(zhí)行操作時(shí)券时,CPU不需要先將數(shù)據(jù)從某處內(nèi)存復(fù)制到另一個(gè)特定區(qū)域。這種技術(shù)通常用于通過網(wǎng)絡(luò)傳輸文件時(shí)節(jié)省CPU周期和內(nèi)存帶寬伏伯。

  1. 零拷貝技術(shù)可以減少數(shù)據(jù)拷貝和共享總線操作的次數(shù)橘洞,消除傳輸數(shù)據(jù)在存儲器之間不必要的中間拷貝次數(shù)织中,從而有效地提高數(shù)據(jù)傳輸效率
  2. 零拷貝技術(shù)減少了用戶進(jìn)程地址空間和內(nèi)核地址空間之間因?yàn)樯?下文切換而帶來的開銷(可以看出沒有說不需要拷貝囤热,只是說減少冗余[不必要]的拷貝)。

LinuxI/O機(jī)制及零拷貝介紹

IO中斷與DMA

IO中斷民镜,需要CPU響應(yīng),需要CPU參與适肠,因此效率比較低霍衫。


image.png

用戶進(jìn)程需要讀取磁盤數(shù)據(jù),需要CPU中斷侯养,發(fā)起IO請求敦跌,每次的IO中斷,都帶來CPU的上下文切換逛揩。

因此出現(xiàn)了——DMA柠傍。
DMA(Direct Memory Access,直接內(nèi)存存取) 是所有現(xiàn)代電腦的重要特色辩稽,它允許不同速度的硬件裝置來溝通惧笛,而不需要依賴于CPU 的大量中斷負(fù)載。

DMA控制器逞泄,接管了數(shù)據(jù)讀寫請求患整,減少CPU的負(fù)擔(dān)。這樣一來喷众,CPU能高效工作了各谚。
現(xiàn)代硬盤基本都支持DMA。

image.png

Linux IO流程

實(shí)際因此IO讀取到千,涉及兩個(gè)過程:
1昌渤、DMA等待數(shù)據(jù)準(zhǔn)備好,把磁盤數(shù)據(jù)讀取到操作系統(tǒng)內(nèi)核緩沖區(qū)父阻;
2愈涩、用戶進(jìn)程,將內(nèi)核緩沖區(qū)的數(shù)據(jù)copy到用戶空間加矛。

這兩個(gè)過程履婉,都是阻塞的。

image.png

傳統(tǒng)數(shù)據(jù)傳送

image.png

比如:讀取文件斟览,再用socket發(fā)送出去
傳統(tǒng)方式實(shí)現(xiàn):
先讀取毁腿、再發(fā)送,實(shí)際經(jīng)過1~4四次copy苛茂。

buffer = File.read 
Socket.send(buffer)

1已烤、第一次:將磁盤文件,讀取到操作系統(tǒng)內(nèi)核緩沖區(qū)妓羊;
2胯究、第二次:將內(nèi)核緩沖區(qū)的數(shù)據(jù),copy到application應(yīng)用程序的buffer躁绸;
3裕循、第三步:將application應(yīng)用程序buffer中的數(shù)據(jù)臣嚣,copy到socket網(wǎng)絡(luò)發(fā)送緩沖區(qū)(屬于操作系統(tǒng)內(nèi)核的緩沖區(qū));
4剥哑、第四次:將socket buffer的數(shù)據(jù)硅则,copy到網(wǎng)卡,由網(wǎng)卡進(jìn)行網(wǎng)絡(luò)傳輸株婴。

image.png

傳統(tǒng)方式怎虫,讀取磁盤文件并進(jìn)行網(wǎng)絡(luò)發(fā)送,經(jīng)過的四次數(shù)據(jù)copy是非常繁瑣的困介。實(shí)際IO讀寫大审,需要進(jìn)行IO中斷,需要CPU響應(yīng)中斷(帶來上下文切換)逻翁,盡管后來引入DMA來接管CPU的中斷請求饥努,但四次copy是存在“不必要的拷貝”的捡鱼。

重新思考傳統(tǒng)IO方式八回,會注意到實(shí)際上并不需要第二個(gè)和第三個(gè)數(shù)據(jù)副本。應(yīng)用程序除了緩存數(shù)據(jù)并將其傳輸回套接字緩沖區(qū)之外什么都不做驾诈。相反缠诅,數(shù)據(jù)可以直接從讀緩沖區(qū)傳輸?shù)教捉幼志彌_區(qū)。

顯然乍迄,第二次和第三次數(shù)據(jù)copy 其實(shí)在這種場景下沒有什么幫助反而帶來開銷管引,這也正是零拷貝出現(xiàn)的背景和意義。

傳統(tǒng)數(shù)據(jù)傳送所消耗的成本:4次拷貝闯两,4次上下文切換褥伴。

4次拷貝,其中兩次是DMA copy漾狼,兩次是CPU copy重慢。如下圖所示
拷貝是個(gè)IO過程,需要系統(tǒng)調(diào)用逊躁。

image.png

注意一點(diǎn)的是 內(nèi)核從磁盤上面讀取數(shù)據(jù) 是 不消耗CPU時(shí)間的似踱,是通過磁盤控制器完成;稱之為DMA Copy稽煤。

網(wǎng)卡發(fā)送也用DMA核芽。

零拷貝的出現(xiàn)

目的:減少IO流程中不必要的拷貝
零拷貝需要OS支持,也就是需要kernel暴露api酵熙。虛擬機(jī)不能操作內(nèi)核轧简,

image.png

Linux支持的(常見)零拷貝

一、mmap內(nèi)存映射

data loaded from disk is stored in a kernel buffer by DMA copy. Then the pages of the application buffer are mapped to the kernel buffer, so that the data copy between kernel buffers and application buffers are omitted.

DMA加載磁盤數(shù)據(jù)到kernel buffer后匾二,應(yīng)用程序緩沖區(qū)(application buffers)和內(nèi)核緩沖區(qū)(kernel buffer)進(jìn)行映射哮独,數(shù)據(jù)再應(yīng)用緩沖區(qū)和內(nèi)核緩存區(qū)的拷貝就能省略庐橙。

image.png

mmap內(nèi)存映射將會經(jīng)歷:3次拷貝: 1次cpu copy,2次DMA copy借嗽;
以及4次上下文切換

二态鳖、sendfile

linux 2.1支持的sendfile

when calling the sendfile() system call, data are fetched from disk and copied into a kernel buffer by DMA copy. Then data are copied directly from the kernel buffer to the socket buffer. Once all data are copied into the socket buffer, the sendfile() system call will return to indicate the completion of data transfer from the kernel buffer to socket buffer. Then, data will be copied to the buffer on the network card and transferred to the network.

當(dāng)調(diào)用sendfile()時(shí),DMA將磁盤數(shù)據(jù)復(fù)制到kernel buffer恶导,然后將內(nèi)核中的kernel buffer直接拷貝到socket buffer浆竭;

一旦數(shù)據(jù)全都拷貝到socket buffer,sendfile()系統(tǒng)調(diào)用將會return惨寿、代表數(shù)據(jù)轉(zhuǎn)化的完成邦泄。
socket buffer里的數(shù)據(jù)就能在網(wǎng)絡(luò)傳輸了。

image.png

sendfile會經(jīng)歷:3次拷貝裂垦,1次CPU copy 2次DMA copy顺囊;
以及2次上下文切換

三、Sendfile With DMA Scatter/Gather Copy

Then by using the DMA scatter/gather operation, the network interface card can gather all the data from different memory locations and store the assembled packet in the network card buffer.

Scatter/Gather可以看作是sendfile的增強(qiáng)版蕉拢,批量sendfile特碳。

image.png

Scatter/Gather會經(jīng)歷2次拷貝: 0次cpu copy,2次DMA copy

IO請求批量化

DMA scatter/gather:需要DMA控制器支持的晕换。
DMA工作流程:cpu發(fā)送IO請求給DMA午乓,DMA然后讀取數(shù)據(jù)。
IO請求:相當(dāng)于可以看作包含一個(gè)物理地址闸准。
從一系列物理地址(10)讀數(shù)據(jù):普通的DMA (10請求)
dma scatter/gather:一次給10個(gè)物理地址益愈, 一個(gè)請求就可以(批量處理)。

Linux零拷貝機(jī)制對比

無論是傳統(tǒng)IO方式夷家,還是引入零拷貝之后蒸其,2次DMA copy 是都少不了的。因?yàn)閮纱蜠MA都是依賴硬件完成的库快。

image.png

零拷貝的廣義狹義之分

image.png

實(shí)際上摸袁,零拷貝時(shí)有廣義和狹義之分的。

廣義零拷貝: 能減少拷貝次數(shù)缺谴,減少不必要的數(shù)據(jù)拷貝但惶,就算作“零拷貝”。
這是目前湿蛔,對零拷貝最為廣泛的定義膀曾,我們需要知道的是,這是廣義上的零拷貝阳啥,并不是操作系統(tǒng) 意義上的零拷貝添谊。

零拷貝的廣義性
最早的零拷貝定義,來源于

Linux 2.4內(nèi)核新增 sendfile 系統(tǒng)調(diào)用察迟,提供了零拷貝斩狱。磁盤數(shù)據(jù)通過 DMA 拷貝到內(nèi)核態(tài) Buffer 后耳高,直接通過 DMA 拷貝到 NIC Buffer(socket buffer),無需 CPU 拷貝所踊。這也是零拷貝這一說法的來源泌枪。這是真正操作系統(tǒng) 意義上的零拷貝(也就是狹義零拷貝)。

但是我們知道秕岛,由OS內(nèi)核提供的 操作系統(tǒng)意義上的零拷貝碌燕,發(fā)展到目前也并沒有很多種,也就是這樣的零拷貝并不是很多继薛;

隨著發(fā)展修壕,零拷貝的概念得到了延伸,就是目前的減少不必要的數(shù)據(jù)拷貝都算作零拷貝的范疇遏考;

Java零拷貝機(jī)制解析

Linux提供的領(lǐng)拷貝技術(shù) Java并不是全支持慈鸠,支持2種(內(nèi)存映射mmap、sendfile)灌具;

image.png

NIO提供的內(nèi)存映射 MappedByteBuffer

  • 首先要說明的是青团,JavaNlO中 的Channel (通道)就相當(dāng)于操作系統(tǒng)中的內(nèi)核緩沖區(qū),有可能是讀緩沖區(qū)稽亏,也有可能是網(wǎng)絡(luò)緩沖區(qū)壶冒,而Buffer就相當(dāng)于操作系統(tǒng)中的用戶緩沖區(qū)缕题。
MappedByteBuffer mappedByteBuffer = new RandomAccessFile(file, "r") 
                                 .getChannel() 
                                .map(FileChannel.MapMode.READ_ONLY, 0, len);

底層就是調(diào)用Linux mmap()實(shí)現(xiàn)的截歉。

NIO中的FileChannel.map()方法其實(shí)就是采用了操作系統(tǒng)中的內(nèi)存映射方式,底層就是調(diào)用Linux mmap()實(shí)現(xiàn)的烟零。

將內(nèi)核緩沖區(qū)的內(nèi)存和用戶緩沖區(qū)的內(nèi)存做了一個(gè)地址映射瘪松。這種方式適合讀取大文件,同時(shí)也能對文件內(nèi)容進(jìn)行更改锨阿,但是如果其后要通過SocketChannel發(fā)送宵睦,還是需要CPU進(jìn)行數(shù)據(jù)的拷貝。

使用MappedByteBuffer墅诡,小文件壳嚎,效率不高;一個(gè)進(jìn)程訪問末早,效率也不高烟馅。

MappedByteBuffer只能通過調(diào)用FileChannel的map()取得,再沒有其他方式然磷。
FileChannel.map()是抽象方法郑趁,具體實(shí)現(xiàn)是在 FileChannelImpl.c 可自行查看JDK源碼,其map0()方法就是調(diào)用了Linux內(nèi)核的mmap的API姿搜。
使用 MappedByteBuffer類要注意的是:mmap的文件映射寡润,在full gc時(shí)才會進(jìn)行釋放捆憎。當(dāng)close時(shí),需要手動(dòng)清除內(nèi)存映射文件梭纹,可以反射調(diào)用sun.misc.Cleaner方法躲惰。

NIO提供的sendfile

  • FileChannel.transferTo()方法直接將當(dāng)前通道內(nèi)容傳輸?shù)搅硪粋€(gè)通道,沒有涉及到Buffer的任何操作变抽,NIO中 的Buffer是JVM堆或者堆外內(nèi)存礁扮,但不論如何他們都是操作系統(tǒng)內(nèi)核空間的內(nèi)存
  • transferTo()的實(shí)現(xiàn)方式就是通過系統(tǒng)調(diào)用sendfile() (當(dāng)然這是Linux中的系統(tǒng)調(diào)用)
//使用sendfile:讀取磁盤文件,并網(wǎng)絡(luò)發(fā)送
FileChannel sourceChannel = new RandomAccessFile(source, "rw").getChannel();
SocketChannel socketChannel = SocketChannel.open(sa);
sourceChannel.transferTo(0, sourceChannel.size(), socketChannel);

ZeroCopyFile實(shí)現(xiàn)文件復(fù)制

class ZeroCopyFile {

    public void copyFile(File src, File dest) {
        try (FileChannel srcChannel = new FileInputStream(src).getChannel();
             FileChannel destChannel = new FileInputStream(dest).getChannel()) {

            srcChannel.transferTo(0, srcChannel.size(), destChannel);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Java NIO提供的FileChannel.transferTo 和 transferFrom 并不保證一定能使用零拷貝瞬沦。實(shí)際上是否能使用零拷貝與操作系統(tǒng)相關(guān)太伊,如果操作系統(tǒng)提供 sendfile 這樣的零拷貝系統(tǒng)調(diào)用,則這兩個(gè)方法會通過這樣的系統(tǒng)調(diào)用充分利用零拷貝的優(yōu)勢逛钻,否則并不能通過這兩個(gè)方法本身實(shí)現(xiàn)零拷貝僚焦。

Kafka中的零拷貝

Kafka兩個(gè)重要過程都使用了零拷貝技術(shù),且都是操作系統(tǒng)層面的狹義零拷貝曙痘,一是Producer生產(chǎn)的數(shù)據(jù)存到broker芳悲,二是 Consumer從broker讀取數(shù)據(jù)。

  • Producer生產(chǎn)的數(shù)據(jù)持久化到broker边坤,采用mmap文件映射名扛,實(shí)現(xiàn)順序的快速寫入;
  • Customer從broker讀取數(shù)據(jù)茧痒,采用sendfile肮韧,將磁盤文件讀到OS內(nèi)核緩沖區(qū)后,直接轉(zhuǎn)到socket buffer進(jìn)行網(wǎng)絡(luò)發(fā)送旺订。

Netty中的零拷貝
Netty中的Zero-copy與上面我們所提到到OS層面上的Zero-copy不太一樣, Netty的Zero-copy完全是在用戶態(tài)(Java層面)的弄企,它的Zero-copy的更多的是偏向于優(yōu)化數(shù)據(jù)操作這樣的概念

Netty的Zero-copy體現(xiàn)在如下幾個(gè)個(gè)方面:

  • Netty提供了CompositeByteBuf類,它可以將多個(gè)ByteBuf合并為一個(gè)邏輯上的ByteBuf区拳,避免了各個(gè)ByteBuf之間的拷貝拘领。
  • 通過wrap操作,我們可以將byte[]數(shù)組樱调、ByteBuf约素、 ByteBuffer 等包裝成一個(gè) Netty ByteBuf對象,進(jìn)而避免了拷貝操作笆凌。
  • ByteBuf支持slice 操作圣猎,因此可以將ByteBuf分解為多個(gè)共享同一個(gè)存儲區(qū)域的ByteBuf,避免了內(nèi)存的拷貝菩颖。
  • 通過FileRegion包裝的FileChannel.tranferTo實(shí)現(xiàn)文件傳輸样漆,可以直接將文件緩沖區(qū)的數(shù)據(jù)發(fā)送到目標(biāo)Channel,避免了傳統(tǒng)通過循環(huán)write方式導(dǎo)致的內(nèi)存拷貝問題晦闰。

前三個(gè)都是 廣義零拷貝放祟,都是減少不必要數(shù)據(jù)copy鳍怨;偏向于應(yīng)用層數(shù)據(jù)優(yōu)化的操作。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末跪妥,一起剝皮案震驚了整個(gè)濱河市鞋喇,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌眉撵,老刑警劉巖侦香,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異纽疟,居然都是意外死亡罐韩,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進(jìn)店門污朽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來散吵,“玉大人,你說我怎么就攤上這事蟆肆》溃” “怎么了?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵炎功,是天一觀的道長枚冗。 經(jīng)常有香客問我,道長蛇损,這世上最難降的妖魔是什么赁温? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮州藕,結(jié)果婚禮上束世,老公的妹妹穿的比我還像新娘。我一直安慰自己床玻,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布沉帮。 她就那樣靜靜地躺著锈死,像睡著了一般。 火紅的嫁衣襯著肌膚如雪穆壕。 梳的紋絲不亂的頭發(fā)上待牵,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天,我揣著相機(jī)與錄音喇勋,去河邊找鬼缨该。 笑死,一個(gè)胖子當(dāng)著我的面吹牛川背,可吹牛的內(nèi)容都是我干的贰拿。 我是一名探鬼主播蛤袒,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼膨更!你這毒婦竟也來了妙真?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤荚守,失蹤者是張志新(化名)和其女友劉穎珍德,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體矗漾,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡锈候,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了敞贡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片晴及。...
    茶點(diǎn)故事閱讀 39,953評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖嫡锌,靈堂內(nèi)的尸體忽然破棺而出虑稼,到底是詐尸還是另有隱情,我是刑警寧澤势木,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布蛛倦,位于F島的核電站,受9級特大地震影響啦桌,放射性物質(zhì)發(fā)生泄漏溯壶。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一甫男、第九天 我趴在偏房一處隱蔽的房頂上張望且改。 院中可真熱鬧,春花似錦板驳、人聲如沸又跛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽慨蓝。三九已至,卻和暖如春端幼,著一層夾襖步出監(jiān)牢的瞬間礼烈,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工婆跑, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留此熬,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像犀忱,于是被迫代替她去往敵國和親募谎。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評論 2 355

推薦閱讀更多精彩內(nèi)容