亚洲欧美日韩丝袜另类_欧美va亚洲va国产综合_亚洲香蕉毛片久久网站老妇人_国产成+人+综合+亚洲欧美丁香花

您的位置:首頁>智東西 >

搞明白什么是零拷貝,就是這么簡單

來源:古時的風箏  

我們總會在各種地方看到零拷貝,那零拷貝到底是個什么東西。

接下來,讓我們來理一理啊。

拷貝說的是計算機里的 I/O 操作,也就是數據的讀寫操作。計算機可是一個復雜的家伙,包括軟件和硬件兩大部分,軟件主要指操作系統、驅動程序和應用程序。硬件那就多了,CPU、內存、硬盤等等一大堆東西。


(資料圖片僅供參考)

這么復雜的設備要進行讀寫操作,其中繁瑣和復雜程度可想而知。

傳統I/O的讀寫過程

如果要了解零拷貝,那就必須要知道一般情況下,計算機是如何讀寫數據的,我把這種情況稱為傳統 I/O。

數據讀寫的發起者是計算機中的應用程序,比如我們常用的瀏覽器、辦公軟件、音視頻軟件等。

而數據的來源呢,一般是硬盤、外部存儲設備或者是網絡套接字(也就是網絡上的數據通過網口+網卡的處理)。

過程本來是很復雜的,所以大學課程里要通過《操作系統》、《計算機組成原理》來專門講計算機的軟硬件。

簡化版讀操作流程

那么細的沒辦法講來,所以,我們把這個讀寫過程簡化一下,忽略大多數細節,只講流程。

圖片

上圖是應用程序進行一次讀操作的過程。

應用程序先發起讀操作,準備讀取數據了;內核將數據從硬盤或外部存儲讀取到內核緩沖區;內核將數據從內核緩沖區拷貝到用戶緩沖區;應用程序讀取用戶緩沖區的數據進行處理加工;詳細的讀寫操作流程

下面是一個更詳細的 I/O 讀寫過程。這個圖可好用極了,我會借助這個圖來厘清 I/O 操作的一些基礎但非常重要的概念。

圖片

先看一下這個圖,上面紅粉色部分是讀操作,下面藍色部分是寫操作。

如果一下子看著有點兒迷糊的話,沒關系,看看下面幾個概念就清楚了。

應用程序

就是安裝在操作系統上的各種應用。

系統內核

系統內核是一些列計算機的核心資源的集合,不僅包括CPU、總線這些硬件設備,也包括進程管理、文件管理、內存管理、設備驅動、系統調用等一些列功能。

外部存儲

外部存儲就是指硬盤、U盤等外部存儲介質。

內核態內核態是操作系統內核運行的模式,當操作系統內核執行特權指令時,處于內核態。在內核態下,操作系統內核擁有最高權限,可以訪問計算機的所有硬件資源和敏感數據,執行特權指令,控制系統的整體運行。內核態提供了操作系統管理和控制計算機硬件的能力,它負責處理系統調用、中斷、硬件異常等核心任務。用戶態

這里的用戶可以理解為應用程序,這個用戶是對于計算機的內核而言的,對于內核來說,系統上的各種應用程序會發出指令來調用內核的資源,這時候,應用程序就是內核的用戶。

用戶態是應用程序運行的模式,當應用程序執行普通的指令時,處于用戶態。在用戶態下,應用程序只能訪問自己的內存空間和受限的硬件資源,無法直接訪問操作系統的敏感數據或控制計算機的硬件設備。用戶態提供了一種安全的運行環境,確保應用程序之間相互隔離,防止惡意程序對系統造成影響。模式切換

計算機為了安全性考慮,區分了內核態和用戶態,應用程序不能直接調用內核資源,必須要切換到內核態之后,讓內核來調用,內核調用完資源,再返回給應用程序,這個時候,系統在切換會用戶態,應用程序在用戶態下才能處理數據。

上述過程其實一次讀和一次寫都分別發生了兩次模式切換。

圖片

內核緩沖區

內核緩沖區指內存中專門用來給內核直接使用的內存空間。可以把它理解為應用程序和外部存儲進行數據交互的一個中間介質。

應用程序想要讀外部數據,要從這里讀。應用程序想要寫入外部存儲,要通過內核緩沖區。

用戶緩沖區

用戶緩沖區可以理解為應用程序可以直接讀寫的內存空間。因為應用程序沒法直接到內核讀寫數據, 所以應用程序想要處理數據,必須先通過用戶緩沖區。

磁盤緩沖區

磁盤緩沖區是計算機內存中用于暫存從磁盤讀取的數據或將數據寫入磁盤之前的臨時存儲區域。它是一種優化磁盤 I/O 操作的機制,通過利用內存的快速訪問速度,減少對慢速磁盤的頻繁訪問,提高數據讀取和寫入的性能和效率。

PageCachePageCache 是 Linux 內核對文件系統進行緩存的一種機制。它使用空閑內存來緩存從文件系統讀取的數據塊,加速文件的讀取和寫入操作。當應用程序或進程讀取文件時,數據會首先從文件系統讀取到 PageCache 中。如果之后再次讀取相同的數據,就可以直接從 PageCache 中獲取,避免了再次訪問文件系統。同樣,當應用程序或進程將數據寫入文件時,數據會先暫存到 PageCache 中,然后由 Linux 內核異步地將數據寫入磁盤,從而提高寫入操作的效率。再說數據讀寫操作流程

上面弄明白了這幾個概念后,再回過頭看一下那個流程圖,是不是就清楚多了。

讀操作首先應用程序向內核發起讀請求,這時候進行一次模式切換了,從用戶態切換到內核態;內核向外部存儲或網絡套接字發起讀操作;將數據寫入磁盤緩沖區;系統內核將數據從磁盤緩沖區拷貝到內核緩沖區,順便再將一份(或者一部分)拷貝到 PageCache;內核將數據拷貝到用戶緩沖區,供應用程序處理。此時又進行一次模態切換,從內核態切換回用戶態;寫操作應用程序向內核發起寫請求,這時候進行一次模式切換了,從用戶態切換到內核態;內核將要寫入的數據從用戶緩沖區拷貝到 PageCache,同時將數據拷貝到內核緩沖區;然后內核將數據寫入到磁盤緩沖區,從而寫入磁盤,或者直接寫入網絡套接字。瓶頸在哪里

但是傳統I/O有它的瓶頸,這才是零拷貝技術出現的緣由。瓶頸是啥呢,當然是性能問題,太慢了。尤其是在高并發場景下,I/O性能經常會卡脖子。

那是什么地方耗時了呢?

數據拷貝

在傳統 I/O 中,數據的傳輸通常涉及多次數據拷貝。數據需要從應用程序的用戶緩沖區復制到內核緩沖區,然后再從內核緩沖區復制到設備或網絡緩沖區。這些數據拷貝過程導致了多次內存訪問和數據復制,消耗了大量的 CPU 時間和內存帶寬。

用戶態和內核態的切換

由于數據要經過內核緩沖區,導致數據在用戶態和內核態之間來回切換,切換過程中會有上下文的切換,如此一來,大大增加了處理數據的復雜性和時間開銷。

每一次操作耗費的時間雖然很小,但是當并發量高了以后,積少成多,也是不小的開銷。所以要提高性能、減少開銷就要從以上兩個問題下手了。

這時候,零拷貝技術就出來解決問題了。

什么是零拷貝

問題出來數據拷貝和模態切換上。

但既然是 I/O 操作,不可能沒有數據拷貝的,只能減少拷貝的次數,還有就是盡量將數據存儲在離應用程序(用戶緩沖區)更近的地方。

而區分用戶態和內核態有其他更重要的原因,不可能單純為了 I/O 效率就改變這種設計吧。那也只能盡量減少切換的次數。

零拷貝的理想狀態就是操作數據不用拷貝,但是顯示情況下并不一定真的就是一次復制操作都沒有,而是盡量減少拷貝操作的次數。

要實現零拷貝,應該從下面這三個方面入手:

盡量減少數據在各個存儲區域的復制操作,例如從磁盤緩沖區到內核緩沖區等;盡量減少用戶態和內核態的切換次數及上下文切換;使用一些優化手段,例如對需要操作的數據先緩存起來,內核中的 PageCache 就是這個作用;實現零拷貝方案直接內存訪問(DMA)

DMA 是一種硬件特性,允許外設(如網絡適配器、磁盤控制器等)直接訪問系統內存,而無需通過 CPU 的介入。在數據傳輸時,DMA 可以直接將數據從內存傳輸到外設,或者從外設傳輸數據到內存,避免了數據在用戶態和內核態之間的多次拷貝。

圖片

DMA1

如上圖所示,內核將數據讀取的大部分數據讀取操作都交個了 DMA 控制器,而空出來的資源就可以去處理其他的任務了。

sendfile

一些操作系統(例如 Linux)提供了特殊的系統調用,如 sendfile,在網絡傳輸文件時實現零拷貝。通過 sendfile,應用程序可以直接將文件數據從文件系統傳輸到網絡套接字或者目標文件,而無需經過用戶緩沖區和內核緩沖區。

如果不用sendfile,如果將A文件寫入B文件。

需要先將A文件的數據拷貝到內核緩沖區,再從內核緩沖區拷貝到用戶緩沖區;然后內核再將用戶緩沖區的數據拷貝到內核緩沖區,之后才能寫入到B文件;

而用了sendfile,用戶緩沖區和內核緩沖區的拷貝都不用了,節省了一大部分的開銷。

共享內存

使用共享內存技術,應用程序和內核可以共享同一塊內存區域,避免在用戶態和內核態之間進行數據拷貝。應用程序可以直接將數據寫入共享內存,然后內核可以直接從共享內存中讀取數據進行傳輸,或者反之。

圖片

通過共享一塊兒內存區域,實現數據的共享。就像程序中的引用對象一樣,實際上就是一個指針、一個地址。

內存映射文件(Memory-mapped Files)

內存映射文件直接將磁盤文件映射到應用程序的地址空間,使得應用程序可以直接在內存中讀取和寫入文件數據,這樣一來,對映射內容的修改就是直接的反應到實際的文件中。

當文件數據需要傳輸時,內核可以直接從內存映射區域讀取數據進行傳輸,避免了數據在用戶態和內核態之間的額外拷貝。

雖然看上去感覺和共享內存沒什么差別,但是兩者的實現方式完全不同,一個是共享地址,一個是映射文件內容。

Java 實現零拷貝的方式

Java 標準的 IO 庫是沒有零拷貝方式的實現的,標準IO就相當于上面所說的傳統模式。只是在 Java 推出的 NIO 中,才包含了一套新的 I/O 類,如ByteBuffer和Channel,它們可以在一定程度上實現零拷貝。

ByteBuffer:可以直接操作字節數據,避免了數據在用戶態和內核態之間的復制。

Channel:支持直接將數據從文件通道或網絡通道傳輸到另一個通道,實現文件和網絡的零拷貝傳輸。

借助這兩種對象,結合 NIO 中的API,我們就能在 Java 中實現零拷貝了。

首先我們先用傳統 IO 寫一個方法,用來和后面的 NIO 作對比,這個程序的目的很簡單,就是將一個100M左右的PDF文件從一個目錄拷貝到另一個目錄。

public static void ioCopy() {  try {    File sourceFile = new File(SOURCE_FILE_PATH);    File targetFile = new File(TARGET_FILE_PATH);    try (FileInputStream fis = new FileInputStream(sourceFile);         FileOutputStream fos = new FileOutputStream(targetFile)) {      byte[] buffer = new byte[1024];      int bytesRead;      while ((bytesRead = fis.read(buffer)) != -1) {        fos.write(buffer, 0, bytesRead);      }    }    System.out.println("傳輸 " + formatFileSize(sourceFile.length()) + " 字節到目標文件");  } catch (IOException e) {    e.printStackTrace();  }}

下面是這個拷貝程序的執行結果,109.92M,耗時1.29秒。

傳輸 109.92 M 字節到目標文件 耗時: 1.290 秒

FileChannel.transferTo() 和 transferFrom()

FileChannel 是一個用于文件讀寫、映射和操作的通道,同時它在并發環境下是線程安全的,基于 FileInputStream、FileOutputStream 或者 RandomAccessFile 的 getChannel() 方法可以創建并打開一個文件通道。FileChannel 定義了 transferFrom() 和 transferTo() 兩個抽象方法,它通過在通道和通道之間建立連接實現數據傳輸的。

這兩個方法首選用 sendfile 方式,只要當前操作系統支持,就用 sendfile,例如Linux或MacOS。如果系統不支持,例如windows,則采用內存映射文件的方式實現。

transferTo()

下面是一個 transferTo 的例子,仍然是拷貝那個100M左右的 PDF,我的系統是 MacOS。

public static void nioTransferTo() {  try {    File sourceFile = new File(SOURCE_FILE_PATH);    File targetFile = new File(TARGET_FILE_PATH);    try (FileChannel sourceChannel = new RandomAccessFile(sourceFile, "r").getChannel();         FileChannel targetChannel = new RandomAccessFile(targetFile, "rw").getChannel()) {      long transferredBytes = sourceChannel.transferTo(0, sourceChannel.size(), targetChannel);      System.out.println("傳輸 " + formatFileSize(transferredBytes) + " 字節到目標文件");    }  } catch (IOException e) {    e.printStackTrace();  }}

只耗時0.536秒,快了一倍。

傳輸 109.92 M 字節到目標文件 耗時: 0.536 秒

transferFrom()

下面是一個 transferFrom 的例子,仍然是拷貝那個100M左右的 PDF,我的系統是 MacOS。

public static void nioTransferFrom() {  try {    File sourceFile = new File(SOURCE_FILE_PATH);    File targetFile = new File(TARGET_FILE_PATH);    try (FileChannel sourceChannel = new RandomAccessFile(sourceFile, "r").getChannel();         FileChannel targetChannel = new RandomAccessFile(targetFile, "rw").getChannel()) {      long transferredBytes = targetChannel.transferFrom(sourceChannel, 0, sourceChannel.size());      System.out.println("傳輸 " + formatFileSize(transferredBytes) + " 字節到目標文件");    }  } catch (IOException e) {    e.printStackTrace();  }}

執行時間

傳輸 109.92 M 字節到目標文件 耗時: 0.603 秒

Memory-Mapped Files

Java 的 NIO 也支持內存映射文件(Memory-mapped Files),通過FileChannel.map()實現。

下面是一個FileChannel.map()的例子,仍然是拷貝那個100M左右的 PDF,我的系統是 MacOS。

public static void nioMap(){        try {            File sourceFile = new File(SOURCE_FILE_PATH);            File targetFile = new File(TARGET_FILE_PATH);            try (FileChannel sourceChannel = new RandomAccessFile(sourceFile, "r").getChannel();                 FileChannel targetChannel = new RandomAccessFile(targetFile, "rw").getChannel()) {                long fileSize = sourceChannel.size();                MappedByteBuffer buffer = sourceChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileSize);                targetChannel.write(buffer);                System.out.println("傳輸 " + formatFileSize(fileSize) + " 字節到目標文件");            }        } catch (IOException e) {            e.printStackTrace();        }    }

執行時間:

傳輸 109.92 M 字節到目標文件 耗時: 0.663 秒

關鍵詞:

最新文章
亚洲欧美日韩丝袜另类_欧美va亚洲va国产综合_亚洲香蕉毛片久久网站老妇人_国产成+人+综合+亚洲欧美丁香花

      国产一区二区三区蝌蚪| 国产午夜精品在线观看| 亚洲国产精品av| 色综合久久久网| 精品国内片67194| 夜夜亚洲天天久久| 国产91精品入口| 亚洲三级在线观看| 正在播放一区二区| 亚洲视频一区在线观看| 国产自产2019最新不卡| 国产精品伦理一区二区| 欧美日韩综合一区| 亚洲欧美一区二区视频| 国产一区二区三区| ...av二区三区久久精品| 久久久久久97三级| 一本色道综合亚洲| 中文字幕成人网| 激情综合亚洲精品| 国产精品不卡视频| 日韩欧美在线1卡| 亚洲成a人片在线观看中文| 99re66热这里只有精品3直播| 五月天中文字幕一区二区| 国产亚洲一二三区| 国产资源精品在线观看| 亚洲免费色视频| 久久影院电视剧免费观看| 奇米影视7777精品一区二区| 中文字幕精品综合| 欧美一区二区三区啪啪| 亚洲bdsm女犯bdsm网站| 国产欧美精品一区| 3751色影院一区二区三区| 一区二区三区中文在线| 99re这里只有精品首页| 欧美三级一区二区| 亚洲综合区在线| 久久久激情视频| 欧美电影一区二区| 天天色天天操综合| 国产精品少妇自拍| 精品免费视频一区二区| 裸体一区二区三区| 亚洲美女一区二区三区| 国产喂奶挤奶一区二区三区| 国产美女av一区二区三区| 亚洲一区二区免费视频| 国产精品久久二区二区| a级高清视频欧美日韩| 欧美在线视频你懂得| 夜夜夜精品看看| 日本一区二区动态图| 日韩精品专区在线| 六月丁香婷婷久久| 一二三四社区欧美黄| 国产精品久久久久婷婷| 成人高清av在线| 欧美色图在线观看| 视频一区视频二区中文字幕| 国产精品国产三级国产普通话99| 精品国产乱码久久久久久图片| 精品一区二区三区久久久| 一区二区三区四区视频精品免费| 国产精品亲子乱子伦xxxx裸| 99综合电影在线视频| 91精品国产综合久久久久| 蜜桃精品视频在线观看| 久久综合久久综合久久综合| 3751色影院一区二区三区| 久久国产精品72免费观看| 亚洲高清免费视频| 亚洲自拍偷拍av| 国产精品久久久久影院老司| 久久精品夜色噜噜亚洲aⅴ| aaa亚洲精品一二三区| 欧美一个色资源| 国产精品1区二区.| 欧美三级三级三级| 蜜臀a∨国产成人精品| 亚洲成人高清在线| 亚洲福利国产精品| 亚洲最新在线观看| 一级中文字幕一区二区| 亚洲蜜臀av乱码久久精品蜜桃| 亚洲欧美日韩国产综合| 国产精品久久久久久久久搜平片| 中文字幕va一区二区三区| 久久久久久久久久久黄色| 久久天天做天天爱综合色| 99久久精品国产精品久久| 精品少妇一区二区三区视频免付费| 国产成人鲁色资源国产91色综| 欧美精选午夜久久久乱码6080| 久久99国内精品| 欧美日产在线观看| 国产精品亚洲视频| 91精品国产91久久久久久最新毛片 | 色噜噜久久综合| 美女视频黄频大全不卡视频在线播放| 天天操天天综合网| 麻豆国产精品一区二区三区| 精品视频免费在线| 国产寡妇亲子伦一区二区| 欧美精品vⅰdeose4hd| 国产成人综合在线播放| 日韩欧美一二区| 91网站在线观看视频| 国产日韩在线不卡| 国产精品视频线看| 亚洲精品中文字幕乱码三区| 亚洲精品成人精品456| 午夜一区二区三区视频| 天天综合天天做天天综合| 久久国产综合精品| 91精品啪在线观看国产60岁| 成人av在线一区二区三区| 久久久久久免费毛片精品| 国产亚洲福利社区一区| 亚洲色图欧洲色图婷婷| 一区二区理论电影在线观看| 日韩国产在线观看| 欧美日韩在线综合| 成人av在线资源网| 国产精品蜜臀av| 伊人色综合久久天天| 日韩成人午夜电影| 欧美精品乱人伦久久久久久| caoporn国产精品| 中文字幕在线视频一区| 亚洲最大成人网4388xx| 蜜臀va亚洲va欧美va天堂| 欧美精品乱码久久久久久| 99久久亚洲一区二区三区青草| 国产性天天综合网| 亚洲图片激情小说| 日本成人在线网站| 欧美一级国产精品| 国产欧美一区二区三区网站 | 亚洲女性喷水在线观看一区| 天堂午夜影视日韩欧美一区二区| 欧美日韩欧美一区二区| 92国产精品观看| 亚洲欧美日韩国产手机在线| 91精彩视频在线| 成人av电影在线网| 综合激情成人伊人| 色综合久久久久| 成人精品免费网站| 中文字幕五月欧美| 婷婷中文字幕综合| 成人黄页在线观看| 中文字幕一区二区三区四区不卡| 亚洲黄色av一区| 国产麻豆视频一区二区| 中文字幕+乱码+中文字幕一区| 亚洲伊人色欲综合网| 国产精品911| 亚洲欧洲99久久| 欧美亚洲综合在线| 91免费观看在线| 亚洲风情在线资源站| 日韩欧美一区在线| 《视频一区视频二区| 国产专区综合网| 中文字幕永久在线不卡| 91国产精品成人| 久久伊99综合婷婷久久伊| 亚洲成a人片在线不卡一二三区| 欧美一区二区观看视频| 中文字幕日本乱码精品影院| 久久精品72免费观看| 国产日本欧美一区二区| 婷婷开心久久网| 久久亚洲一区二区三区四区| 婷婷国产v国产偷v亚洲高清| 精品久久一区二区三区| 亚洲午夜日本在线观看| av中文字幕不卡| 天涯成人国产亚洲精品一区av| 日韩精品专区在线影院重磅| 夜夜嗨av一区二区三区中文字幕| 成人黄色在线视频| 亚洲国产视频在线| 精品国产百合女同互慰| 亚洲国产成人porn| 91美女蜜桃在线| 免费在线观看一区| 中文一区在线播放| 欧美日韩精品一区二区天天拍小说| 国产欧美日韩不卡免费| 韩国视频一区二区| 亚洲三级电影全部在线观看高清| 91麻豆精品国产自产在线观看一区| 亚洲欧美怡红院| 成人国产一区二区三区精品| 亚洲成人精品一区| 国产欧美一区二区精品性色| 日本精品一区二区三区四区的功能|