溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

零拷貝的原理以及java實(shí)現(xiàn)方式

發(fā)布時(shí)間:2021-08-13 13:43:26 來(lái)源:億速云 閱讀:145 作者:chen 欄目:開(kāi)發(fā)技術(shù)

本篇內(nèi)容介紹了“零拷貝的原理以及java實(shí)現(xiàn)方式”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

目錄
  • 零拷貝

  • 傳統(tǒng)I/O操作存在的性能問(wèn)題

  • 零拷貝技術(shù)原理

    • 虛擬內(nèi)存

    • mmap/write 方式

    • sendfile 方式

    • 帶有 scatter/gather 的 sendfile方式

    • splice 方式

  • 總結(jié)

    零拷貝

    零拷貝(Zero-Copy)是一種 I/O 操作優(yōu)化技術(shù),可以快速高效地將數(shù)據(jù)從文件系統(tǒng)移動(dòng)到網(wǎng)絡(luò)接口,而不需要將其從內(nèi)核空間復(fù)制到用戶空間。其在 FTP 或者 HTTP 等協(xié)議中可以顯著地提升性能。但是需要注意的是,并不是所有的操作系統(tǒng)都支持這一特性,目前只有在使用 NIO 和 Epoll 傳輸時(shí)才可使用該特性。

    需要注意,它不能用于實(shí)現(xiàn)了數(shù)據(jù)加密或者壓縮的文件系統(tǒng)上,只有傳輸文件的原始內(nèi)容。這類(lèi)原始內(nèi)容也包括加密了的文件內(nèi)容。

    傳統(tǒng)I/O操作存在的性能問(wèn)題

    如果服務(wù)端要提供文件傳輸?shù)墓δ?,我們能想到的最?jiǎn)單的方式是:將磁盤(pán)上的文件讀取出來(lái),然后通過(guò)網(wǎng)絡(luò)協(xié)議發(fā)送給客戶端。

    傳統(tǒng) I/O 的工作方式是,數(shù)據(jù)讀取和寫(xiě)入是從用戶空間到內(nèi)核空間來(lái)回復(fù)制,而內(nèi)核空間的數(shù)據(jù)是通過(guò)操作系統(tǒng)層面的 I/O 接口從磁盤(pán)讀取或?qū)懭搿?/p>

    代碼通常如下,一般會(huì)需要兩個(gè)系統(tǒng)調(diào)用:

    read(file, tmp_buf, len);
    write(socket, tmp_buf, len);

    代碼很簡(jiǎn)單,雖然就兩行代碼,但是這里面發(fā)生了不少的事情。

    零拷貝的原理以及java實(shí)現(xiàn)方式

    首先,期間共發(fā)生了 4 次用戶態(tài)與內(nèi)核態(tài)的上下文切換,因?yàn)榘l(fā)生了兩次系統(tǒng)調(diào)用,一次是 read() ,一次是 write(),每次系統(tǒng)調(diào)用都得先從用戶態(tài)切換到內(nèi)核態(tài),等內(nèi)核完成任務(wù)后,再?gòu)膬?nèi)核態(tài)切換回用戶態(tài)。

    上下文切換到成本并不小,一次切換需要耗時(shí)幾十納秒到幾微秒,雖然時(shí)間看上去很短,但是在高并發(fā)的場(chǎng)景下,這類(lèi)時(shí)間容易被累積和放大,從而影響系統(tǒng)的性能。

    其次,還發(fā)生了 4 次數(shù)據(jù)拷貝,其中兩次是 DMA 的拷貝,另外兩次則是通過(guò) CPU 拷貝的,下面說(shuō)一下這個(gè)過(guò)程:

    • 第一次拷貝,把磁盤(pán)上的數(shù)據(jù)拷貝到操作系統(tǒng)內(nèi)核的緩沖區(qū)里,這個(gè)拷貝的過(guò)程是通過(guò) DMA 搬運(yùn)的。

    • 第二次拷貝,把內(nèi)核緩沖區(qū)的數(shù)據(jù)拷貝到用戶的緩沖區(qū)里,于是我們應(yīng)用程序就可以使用這部分?jǐn)?shù)據(jù)了,這個(gè)拷貝到過(guò)程是由 CPU 完成的。

    • 第三次拷貝,把剛才拷貝到用戶的緩沖區(qū)里的數(shù)據(jù),再拷貝到內(nèi)核的 socket 的緩沖區(qū)里,這個(gè)過(guò)程依然還是由 CPU 搬運(yùn)的。

    • 第四次拷貝,把內(nèi)核的 socket 緩沖區(qū)里的數(shù)據(jù),拷貝到網(wǎng)卡的緩沖區(qū)里,這個(gè)過(guò)程又是由 DMA 搬運(yùn)的。

    這種簡(jiǎn)單又傳統(tǒng)的文件傳輸方式,存在冗余的上文切換和數(shù)據(jù)拷貝,在高并發(fā)系統(tǒng)里是非常糟糕的,多了很多不必要的開(kāi)銷(xiāo),會(huì)嚴(yán)重影響系統(tǒng)性能。

    所以,要想提高文件傳輸?shù)男阅?,就需要減少「用戶態(tài)與內(nèi)核態(tài)的上下文切換」和「內(nèi)存拷貝」的次數(shù)。

    零拷貝技術(shù)原理

    零拷貝主要是用來(lái)解決操作系統(tǒng)在處理 I/O 操作時(shí),頻繁復(fù)制數(shù)據(jù)的問(wèn)題。關(guān)于零拷貝主要技術(shù)有 mmap+write、sendfile和splice等幾種方式。

    虛擬內(nèi)存

    在了解零拷貝技術(shù)之前,先了解虛擬內(nèi)存的概念。
    所有現(xiàn)代操作系統(tǒng)都使用虛擬內(nèi)存,使用虛擬地址取代物理地址,主要有以下幾點(diǎn)好處:

    • 多個(gè)虛擬內(nèi)存可以指向同一個(gè)物理地址。

    • 虛擬內(nèi)存空間可以遠(yuǎn)遠(yuǎn)大于物理內(nèi)存空間。

    利用上述的第一條特性可以優(yōu)化,可以把內(nèi)核空間和用戶空間的虛擬地址映射到同一個(gè)物理地址,這樣在 I/O 操作時(shí)就不需要來(lái)回復(fù)制了。

    如下圖展示了虛擬內(nèi)存的原理。

    零拷貝的原理以及java實(shí)現(xiàn)方式

    mmap/write 方式

    使用mmap/write方式替換原來(lái)的傳統(tǒng)I/O方式,就是利用了虛擬內(nèi)存的特性。下圖展示了mmap/write原理:

    零拷貝的原理以及java實(shí)現(xiàn)方式

    整個(gè)流程的核心區(qū)別就是,把數(shù)據(jù)讀取到內(nèi)核緩沖區(qū)后,應(yīng)用程序進(jìn)行寫(xiě)入操作時(shí),直接把內(nèi)核的Read Buffer的數(shù)據(jù)復(fù)制到Socket Buffer以便寫(xiě)入,這次內(nèi)核之間的復(fù)制也是需要CPU的參與的。

    上述流程就是少了一個(gè) CPU COPY,提升了 I/O 的速度。不過(guò)發(fā)現(xiàn)上下文的切換還是4次并沒(méi)有減少,這是因?yàn)檫€是要應(yīng)用程序發(fā)起write操作。

    那能不能減少上下文切換呢?這就需要sendfile方式來(lái)進(jìn)一步優(yōu)化了。

    sendfile 方式

    從 Linux 2.1 版本開(kāi)始,Linux 引入了 sendfile來(lái)簡(jiǎn)化操作。sendfile方式可以替換上面的mmap/write方式來(lái)進(jìn)一步優(yōu)化。

    sendfile將以下操作:

      mmap();
      write();

    替換為:

     sendfile();

    這樣就減少了上下文切換,因?yàn)樯倭艘粋€(gè)應(yīng)用程序發(fā)起write操作,直接發(fā)起sendfile操作。

    下圖展示了sendfile原理:

    零拷貝的原理以及java實(shí)現(xiàn)方式

    sendfile方式只有三次數(shù)據(jù)復(fù)制(其中只有一次 CPU COPY)以及2次上下文切換。

    那能不能把 CPU COPY 減少到?jīng)]有呢?這樣需要帶有 scatter/gather的sendfile方式了。

    帶有 scatter/gather 的 sendfile方式

    Linux 2.4 內(nèi)核進(jìn)行了優(yōu)化,提供了帶有 scatter/gather 的 sendfile 操作,這個(gè)操作可以把最后一次 CPU COPY 去除。其原理就是在內(nèi)核空間 Read BUffer 和 Socket Buffer 不做數(shù)據(jù)復(fù)制,而是將 Read Buffer 的內(nèi)存地址、偏移量記錄到相應(yīng)的 Socket Buffer 中,這樣就不需要復(fù)制。其本質(zhì)和虛擬內(nèi)存的解決方法思路一致,就是內(nèi)存地址的記錄。

    下圖展示了scatter/gather 的 sendfile 的原理:

    零拷貝的原理以及java實(shí)現(xiàn)方式

    scatter/gather 的 sendfile 只有兩次數(shù)據(jù)復(fù)制(都是 DMA COPY)及 2 次上下文切換。CUP COPY 已經(jīng)完全沒(méi)有。不過(guò)這一種收集復(fù)制功能是需要硬件及驅(qū)動(dòng)程序支持的。

    splice 方式

    splice 調(diào)用和sendfile 非常相似,用戶應(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系統(tǒng)調(diào)用,而splice一直以來(lái)就只是一種機(jī)制,它并不僅限于sendfile的功能。也就是說(shuō) sendfile 是 splice 的一個(gè)子集。

    在 Linux 2.6.17 版本引入了 splice,而在 Linux 2.6.23 版本中, sendfile 機(jī)制的實(shí)現(xiàn)已經(jīng)沒(méi)有了,但是其 API 及相應(yīng)的功能還在,只不過(guò) API 及相應(yīng)的功能是利用了 splice 機(jī)制來(lái)實(shí)現(xiàn)的。

    和 sendfile 不同的是,splice 不需要硬件支持。

    總結(jié)

    無(wú)論是傳統(tǒng)的 I/O 方式,還是引入了零拷貝之后,2 次 DMA copy是都少不了的。因?yàn)閮纱?DMA 都是依賴硬件完成的。所以,所謂的零拷貝,都是為了減少 CPU copy 及減少了上下文的切換。
    下圖展示了各種零拷貝技術(shù)的對(duì)比圖:


    CPU拷貝DMA拷貝系統(tǒng)調(diào)用上下文切換
    傳統(tǒng)方法22read/write4
    內(nèi)存映射12mmap/write4
    sendfile12sendfile2
    scatter/gather copy02sendfile2
    splice02splice0

    “零拷貝的原理以及java實(shí)現(xiàn)方式”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

    向AI問(wèn)一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI