自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

一張圖帶你看懂 IO 零拷貝技術(shù)!

系統(tǒng) Linux
以 Linux 操作系統(tǒng)為例,真正意義上大家比較認(rèn)可的零拷貝主要有 sendfile、splice 等方法,它們完全通過 DMA 控制器來實(shí)現(xiàn)數(shù)據(jù)的拷貝,無需 CPU 來參與數(shù)據(jù)拷貝的過程,這個(gè)過程被稱為零拷貝。

01、背景介紹

相信不少的網(wǎng)友,在很多的博客文章里面,已經(jīng)見到過零拷貝這個(gè)詞,會(huì)不禁的發(fā)出一些疑問,什么是零拷貝?

從字面上我們很容易理解出,零拷貝包含兩個(gè)意思:

  • 拷貝:就是指數(shù)據(jù)從一個(gè)存儲(chǔ)區(qū)域轉(zhuǎn)移到另一個(gè)存儲(chǔ)區(qū)域。
  • 零:它表示拷貝數(shù)據(jù)的次數(shù)為 0。

合起來理解,零拷貝就是不需要將數(shù)據(jù)從一個(gè)存儲(chǔ)區(qū)域復(fù)制到另一個(gè)存儲(chǔ)區(qū)域。

果真是這樣的嗎?

最早的零拷貝定義,來源于 Linux 系統(tǒng)的 sendfile 方法邏輯!

在 Linux 2.4 內(nèi)核中,sendfile 系統(tǒng)調(diào)用方法,可以將磁盤數(shù)據(jù)通過 DMA 拷貝到內(nèi)核態(tài) Buffer 后,再通過 DMA 拷貝到 NIC Buffer(socket buffer),無需 CPU 拷貝,這個(gè)過程被稱之為零拷貝。

從這段描述里面我們可以得知,站在操作系統(tǒng)的角度,零拷貝沒有說不需要拷貝數(shù)據(jù),而是省掉了 CPU 拷貝環(huán)節(jié),減少了不必要的拷貝次數(shù),提升數(shù)據(jù)拷貝效率。

要想深度的了解這里面的原理,我們還得從 IO 拷貝機(jī)制說起!

02、IO 拷貝機(jī)制介紹

2.1、傳統(tǒng)數(shù)據(jù)拷貝流程

以客戶端從服務(wù)器下載文件為例,熟悉服務(wù)端開發(fā)的同學(xué)可能知道,服務(wù)端需要做兩件事:

  • 第一步:從磁盤中讀取文件內(nèi)容
  • 第二步:將文件內(nèi)容通過網(wǎng)絡(luò)傳輸給客戶端

事實(shí)上看似簡單的操作,里面的流程卻沒那么簡單,例如應(yīng)用程序從磁盤中讀取文件內(nèi)容的操作,大體會(huì)經(jīng)過以下幾個(gè)流程:

  • 第一步:用戶應(yīng)用程序調(diào)用 read 方法,向操作系統(tǒng)發(fā)起 IO 請(qǐng)求,CPU 上下文從用戶態(tài)轉(zhuǎn)為內(nèi)核態(tài),完成第一次 CPU 切換
  • 第二步:操作系統(tǒng)通過 DMA 控制器從磁盤中讀數(shù)據(jù),并把數(shù)據(jù)存儲(chǔ)到內(nèi)核緩沖區(qū)
  • 第三步:CPU 把內(nèi)核緩沖區(qū)的數(shù)據(jù),拷貝到用戶緩沖區(qū),同時(shí)上下文從內(nèi)核態(tài)轉(zhuǎn)為用戶態(tài),完成第二次 CPU 切換

整個(gè)讀取數(shù)據(jù)的過程,完成了 1 次 DMA 拷貝,1 次 CPU 拷貝,2 次 CPU 切換;反之寫入數(shù)據(jù)的過程,也是一樣的。

整個(gè)拷貝過程,可以用如下流程圖來描述!

圖片圖片

從上圖,我們可以得出如下結(jié)論,4 次拷貝次數(shù)、4 次上下文切換次數(shù)。

  • 數(shù)據(jù)拷貝次數(shù):2 次 DMA 拷貝,2 次 CPU 拷貝
  • CPU 切換次數(shù):4 次用戶態(tài)和內(nèi)核態(tài)的切換

而實(shí)際 IO 讀寫,有時(shí)候需要進(jìn)行 IO 中斷,同時(shí)也需要 CPU 響應(yīng)中斷,拷貝次數(shù)和切換次數(shù)比預(yù)期的還要多,以至于當(dāng)客戶端進(jìn)行資源文件下載的時(shí)候,傳輸速度總是不盡人意。

那有沒有好的辦法來提升資源拷貝的速度呢?

答案是肯定的,傳統(tǒng)的數(shù)據(jù)拷貝流程還有很大的優(yōu)化空間。

下面我們一起來看看幾種其它的拷貝方式。

2.2、mmap 內(nèi)存映射拷貝流程

mmap 內(nèi)存映射的拷貝,指的是將用戶應(yīng)用程序的緩沖區(qū)和操作系統(tǒng)的內(nèi)核緩沖區(qū)進(jìn)行映射處理,數(shù)據(jù)在內(nèi)核緩沖區(qū)和用戶緩沖區(qū)之間的 CPU 拷貝將其省略,進(jìn)而加快資源拷貝效率。

整個(gè)拷貝過程,可以用如下流程圖來描述!

圖片圖片

mmap 內(nèi)存映射拷貝流程,從上圖可以得出如下結(jié)論:

  • 數(shù)據(jù)拷貝次數(shù):2 次 DMA 拷貝,1 次 CPU 拷貝
  • CPU 切換次數(shù):4 次用戶態(tài)和內(nèi)核態(tài)的切換

整個(gè)過程省掉了數(shù)據(jù)在內(nèi)核緩沖區(qū)和用戶緩沖區(qū)之間的 CPU 拷貝環(huán)節(jié),在實(shí)際的應(yīng)用中,對(duì)資源的拷貝能提升不少。

2.3、Linux 系統(tǒng) sendfile 拷貝流程

在 Linux 2.1 內(nèi)核版本中,引入了一個(gè)系統(tǒng)調(diào)用方法:sendfile。

當(dāng)調(diào)用 sendfile() 時(shí),DMA 將磁盤數(shù)據(jù)復(fù)制到內(nèi)核緩沖區(qū) kernel buffer;然后將內(nèi)核中的 kernel buffer 直接拷貝到 socket buffer;最后利用 DMA 將  socket buffer 通過網(wǎng)卡傳輸給客戶端。

整個(gè)拷貝過程,可以用如下流程圖來描述!

圖片圖片

Linux 系統(tǒng) sendfile 拷貝流程,從上圖可以得出如下結(jié)論:

  • 數(shù)據(jù)拷貝次數(shù):2 次 DMA 拷貝,1 次 CPU 拷貝
  • CPU 切換次數(shù):2 次用戶態(tài)和內(nèi)核態(tài)的切換

相比 mmap 內(nèi)存映射方式,Linux 2.1 內(nèi)核版本中 sendfile 拷貝流程省掉了 2 次用戶態(tài)和內(nèi)核態(tài)的切換,同時(shí)內(nèi)核緩沖區(qū)和用戶緩沖區(qū)也無需建立內(nèi)存映射,對(duì)資源的拷貝能提升不少。

2.4、sendfile With DMA scatter/gather 拷貝流程

在 Linux 2.4 內(nèi)核版本中,對(duì) sendfile 系統(tǒng)方法做了優(yōu)化升級(jí),引入 SG-DMA 技術(shù),需要 DMA 控制器支持。

其實(shí)就是對(duì) DMA 拷貝加入了 scatter/gather 操作,它可以直接從內(nèi)核空間緩沖區(qū)中將數(shù)據(jù)讀取到網(wǎng)卡。使用這個(gè)特點(diǎn)來實(shí)現(xiàn)數(shù)據(jù)拷貝,可以多省去一次 CPU 拷貝。

整個(gè)拷貝過程,可以用如下流程圖來描述!

圖片圖片

Linux 系統(tǒng) sendfile With DMA scatter/gather 拷貝流程,從上圖可以得出如下結(jié)論:

  • 數(shù)據(jù)拷貝次數(shù):2 次 DMA 拷貝,0 次 CPU 拷貝
  • CPU 切換次數(shù):2 次用戶態(tài)和內(nèi)核態(tài)的切換

可以發(fā)現(xiàn),sendfile With DMA scatter/gather 實(shí)現(xiàn)的拷貝,其中 2 次數(shù)據(jù)拷貝都是 DMA 拷貝,全程都沒有通過 CPU 來拷貝數(shù)據(jù),所有的數(shù)據(jù)都是通過 DMA 來進(jìn)行傳輸?shù)?,這就是操作系統(tǒng)真正意義上的零拷貝(Zero-copy) 技術(shù),相比其他拷貝方式,傳輸效率最佳。

2.5、Linux 系統(tǒng) splice 零拷貝流程

在 Linux 2.6.17 內(nèi)核版本中,引入了 splice 系統(tǒng)調(diào)用方法,和 sendfile 方法不同的是,splice 不需要硬件支持。

它將數(shù)據(jù)從磁盤讀取到 OS 內(nèi)核緩沖區(qū)后,內(nèi)核緩沖區(qū)和 socket 緩沖區(qū)之間建立管道來傳輸數(shù)據(jù),避免了兩者之間的 CPU 拷貝操作。

整個(gè)拷貝過程,可以用如下流程圖來描述!

圖片圖片

Linux 系統(tǒng) splice 拷貝流程,從上圖可以得出如下結(jié)論:

  • 數(shù)據(jù)拷貝次數(shù):2 次 DMA 拷貝,0 次 CPU 拷貝
  • CPU 切換次數(shù):2 次用戶態(tài)和內(nèi)核態(tài)的切換

Linux 系統(tǒng) splice 方法邏輯拷貝,也是操作系統(tǒng)真正意義上的零拷貝。

03、IO 拷貝機(jī)制對(duì)比

從上面的 IO 拷貝機(jī)制可以看出,無論是傳統(tǒng) IO 方式,還是引入零拷貝之后,2次 DMA copy 是都少不了的,唯一的區(qū)別就是省掉 CPU 參與環(huán)節(jié)的方式不同。

以 Linux 系統(tǒng)為例,拷貝機(jī)制對(duì)比結(jié)果如下!

圖片圖片

需要注意的地方是,零拷貝所有的方式,都需要操作系統(tǒng)支持,具體采用哪種方式,由操作系統(tǒng)來決定。

04、相關(guān)概念說明

上文中我們提到了幾個(gè)很少見的名詞,比如 DMA,用戶空間,內(nèi)核空間等。它們到底是什么意思呢?

4.1、DMA 介紹

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

4.2、內(nèi)核空間和用戶空間介紹

操作系統(tǒng)的核心是內(nèi)核,與普通的應(yīng)用程序不同,它可以訪問受保護(hù)的內(nèi)存空間,也有訪問底層硬件設(shè)備的權(quán)限。

為了避免用戶進(jìn)程直接操作內(nèi)核,保證內(nèi)核安全,操作系統(tǒng)將虛擬內(nèi)存劃分為兩部分,一部分是內(nèi)核空間(Kernel-space),一部分是用戶空間(User-space)。在 Linux 系統(tǒng)中,內(nèi)核模塊運(yùn)行在內(nèi)核空間,對(duì)應(yīng)的進(jìn)程處于內(nèi)核態(tài);而用戶程序運(yùn)行在用戶空間,對(duì)應(yīng)的進(jìn)程處于用戶態(tài)。

內(nèi)核空間總是駐留在內(nèi)存中,它是為操作系統(tǒng)的內(nèi)核保留的。應(yīng)用程序是不允許直接在該區(qū)域進(jìn)行讀寫或直接調(diào)用內(nèi)核代碼定義的函數(shù)。

當(dāng)啟動(dòng)某個(gè)應(yīng)用程序時(shí),操作系統(tǒng)會(huì)給應(yīng)用程序分配一個(gè)單獨(dú)的用戶空間,其實(shí)就是一個(gè)用戶獨(dú)享的虛擬內(nèi)存,每個(gè)普通的用戶進(jìn)程之間的用戶空間是完全隔離的、不共享的,當(dāng)用戶進(jìn)程結(jié)束的時(shí)候,用戶空間的虛擬內(nèi)存也會(huì)隨之釋放。

同時(shí)處于用戶態(tài)的進(jìn)程不能訪問內(nèi)核空間中的數(shù)據(jù),也不能直接調(diào)用內(nèi)核函數(shù)的,如果要調(diào)用系統(tǒng)資源,就要將進(jìn)程切換到內(nèi)核態(tài),由內(nèi)核程序來進(jìn)行操作。

05、Java 零拷貝實(shí)現(xiàn)介紹

Linux 提供的零拷貝技術(shù),Java 并不是全部支持,目前只支持以下 2 種。

  • mmap(內(nèi)存映射)
  • sendfile

5.1、Java NIO 對(duì) mmap 的支持

Java NIO 有一個(gè)MappedByteBuffer的類,可以用來實(shí)現(xiàn)內(nèi)存映射。它的底層是調(diào)用了 Linux 內(nèi)核的mmap的 API。

實(shí)例代碼如下:

public static void main(String[] args) {
    try {
        FileChannel readChannel = FileChannel.open(Paths.get("a.txt"), StandardOpenOption.READ);
        // 建立內(nèi)存文件映射
        MappedByteBuffer data = readChannel.map(FileChannel.MapMode.READ_ONLY, 0, 1024 * 1024 * 40);
        FileChannel writeChannel = FileChannel.open(Paths.get("b.txt"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
        // 拷貝數(shù)據(jù)
        writeChannel.write(data);

        // 關(guān)閉通道
        readChannel.close();
        writeChannel.close();
    }catch (Exception e){
        System.out.println(e.getMessage());
    }
}

其中MappedByteBuffer的作用,就是將內(nèi)核緩沖區(qū)的內(nèi)存和用戶緩沖區(qū)的內(nèi)存做了一個(gè)地址映射,讀取小文件,效率并不高;但是讀取大文件,效率很高。

5.2、Java NIO 對(duì) sendfile 的支持

Java NIO 中的FileChannel.transferTo方法,底層調(diào)用的就是 Linux 內(nèi)核的sendfile系統(tǒng)調(diào)用方法。Kafka 這個(gè)開源項(xiàng)目就用到它,平時(shí)面試的時(shí)候,如果面試官問起你為什么這么快,就可以提到sendfile零拷貝系統(tǒng)調(diào)用方法來回答。

實(shí)例代碼如下:

public static void main(String[] args) {
    try {
        // 原始文件
        FileChannel srcChannel = FileChannel.open(Paths.get("a.txt"), StandardOpenOption.READ);
        // 目標(biāo)文件
        FileChannel destChannel = FileChannel.open(Paths.get("b.txt"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
        // 拷貝數(shù)據(jù)
        srcChannel.transferTo(0, srcChannel.size(), destChannel);

        // 關(guān)閉通道
        srcChannel.close();
        destChannel.close();
    } catch (Exception e) {
        System.out.println(e.getMessage());
    }
}

Java NIO 提供的FileChannel.transferTo并不保證一定能使用零拷貝。實(shí)際上是否能使用零拷貝與操作系統(tǒng)相關(guān),如果操作系統(tǒng)提供sendfile這樣的零拷貝系統(tǒng)調(diào)用方法,那么會(huì)充分利用sendfile零拷貝的優(yōu)勢(shì),否則并不能實(shí)現(xiàn)零拷貝。

06、小結(jié)

本位主要圍繞零拷貝的邏輯,結(jié)合網(wǎng)友的知識(shí)分享,進(jìn)行一次知識(shí)整理和總結(jié)。

從上面的內(nèi)容總結(jié)可以看出,所謂的零拷貝,其目的并不是說不需要拷貝數(shù)據(jù),而是通過一些手段省略 CPU 拷貝環(huán)節(jié),減少了不必要的拷貝次數(shù),提升數(shù)據(jù)拷貝效率。

以  Linux 操作系統(tǒng)為例,真正意義上大家比較認(rèn)可的零拷貝主要有 sendfile、splice 等方法,它們完全通過 DMA 控制器來實(shí)現(xiàn)數(shù)據(jù)的拷貝,無需 CPU 來參與數(shù)據(jù)拷貝的過程,這個(gè)過程被稱為零拷貝。

但是比較糟心的是,一些機(jī)構(gòu)、團(tuán)隊(duì),在產(chǎn)品推廣中過度包裝零拷貝這個(gè)概念,名曰“采用零拷貝,性能有多高等等...”,這樣的宣傳似乎會(huì)讓很多人覺得很厲害。

作為一線技術(shù)者,應(yīng)該多多深入了解,識(shí)透其真相,弄清楚是偏向于優(yōu)化數(shù)據(jù)操作,還是真正切合場景、靈活運(yùn)用了操作系統(tǒng)意義上的零拷貝,大家可以多深入分析。

07、參考

1、https://zhuanlan.zhihu.com/p/78869158

2、https://zhuanlan.zhihu.com/p/447890038

責(zé)任編輯:武曉燕 來源: 潘志的研發(fā)筆記
相關(guān)推薦

2015-03-10 10:15:27

AppleWatch開發(fā)Swift

2015-06-24 10:51:10

iOS學(xué)習(xí)流程

2018-05-18 18:09:44

人工智能

2024-05-07 08:49:45

微服務(wù)架構(gòu)模式

2014-10-09 09:43:05

虛擬機(jī)遷移

2020-09-12 16:45:49

Git

2019-03-18 15:00:48

SQLJoin用法數(shù)據(jù)庫

2021-11-07 15:04:39

機(jī)器學(xué)習(xí)人工智能數(shù)據(jù)

2022-03-31 11:41:02

DDoS網(wǎng)絡(luò)安全關(guān)鍵信息基礎(chǔ)設(shè)施

2016-11-10 10:03:02

微軟Power BI組件

2014-03-14 09:47:08

手游進(jìn)化產(chǎn)品

2024-02-22 12:20:23

Linux零拷貝技術(shù)

2012-09-10 14:07:58

JavaScriptJS類型

2014-12-02 10:09:05

硅谷比例

2015-04-29 15:51:13

網(wǎng)易有道在線教育

2018-04-03 14:08:15

貓來

2019-05-31 08:54:46

Linux內(nèi)核操作系統(tǒng)

2020-04-01 10:33:39

華為

2014-12-02 10:11:21

硅谷程序員比例

2015-10-28 10:17:22

Html5前端優(yōu)化
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)