日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

深入剖析神秘的“零拷贝”

發布時間:2025/4/5 编程问答 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 深入剖析神秘的“零拷贝” 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

?

https://my.oschina.net/u/4072299/blog/3045755

前言

"零拷貝"這三個字,想必大家多多少少都有聽過吧,這個技術在各種開源組件中都使用了,比如kafka,rocketmq,netty,nginx等等開源框架都在其中引用了這項技術。所以今天想和大家分享一下有關于零拷貝的一些知識。

計算機中數據傳輸

在介紹零拷貝之前我想說下在計算機系統中數據傳輸的方式。數據傳輸系統的發展,為了寫這一部分又祭出了我塵封多年的計算機組成原理:

早期階段:

分散連接,串行工作,程序查詢。 在這個階段,CPU就像個保姆一樣,需要手把手的把數據從I/O接口從讀出然后再送給主存。??這個階段具體流程是:

  • CPU主動啟動I/O設備
  • 然后CPU一直問I/O設備老鐵你準備好了嗎,注意這里是一直詢問。
  • 如果I/O設備告訴了CPU說:我準備好了。CPU就從I/O接口中讀數據。
  • 然后CPU又繼續把這個數據傳給主存,就像快遞員一樣。
  • 這種效率很低數據傳輸過程一直占據著CPU,CPU不能做其他更有意義的事。

    接口模塊和DMA階段

    這一部分介紹的也是我們后面具體

    接口模塊

    在馮諾依曼結構中,每個部件之間均有單獨連線,不僅先多,而且導致擴展I/O設備很不容易,我們上面的早期階段就是這個體系,叫作分散連接。擴展一個I/O設備得連接很多線。所以引入了總線連接方式,將多個設備連接在同一組總線上,構成設備之間的公共傳輸通道。??這個也是現在我們家用電腦或者一些小型計算器的數據交換結構。

    在這種模式下數據交換采用程序中斷的方式,我們上面知道我們啟動I/O設備之后一直在輪詢問I/O設備是否準備好,要是把這個階段去掉了就好了,程序中斷很好的實現了我們的夙愿:

  • CPU主動啟動I/O設備。
  • CPU啟動之后不需要再問I/O,開始做其他事,類似異步化。
  • I/O準備好了之后,通過總線中斷告訴CPU我已經準備好了。
  • CPU進行讀取數據,傳輸給主存中。
  • DMA

    雖然上面的方式雖然提高了CPU的利用率,但是在中斷的時候CPU一樣是被占用的,為了進一步解決CPU占用,又引入了DMA方式,在DMA方式中,主存和I/O設備之間有一條數據通路,這下主存和I/O設備之間交換數據時,就不需要再次中斷CPU。

    一般來說我們只需要關注DMA和中斷兩種即可,下面介紹的都是用來適合大型計算機的一些,這里只說簡單的過一下:

    具有通道結構的階段

    在小型計算機中采用DMA方式可以實現高速I/O設備與主機之間組成數據的交換,但在大中型計算機中,I/O配置繁多,數據傳送平凡,若采用DMA方式會出現一系列問題。

    • 每臺I/O設備都配置專用額DMA接口,不僅增加了硬件成本,而且解決DMA和CPU訪問沖突問題,會使控制變得十分復雜。
    • CPU需要對眾多的DMA接口進行管理,同樣會影響工作效率。

    所以引入了通道,通道用來管理I/O設備以及主存與I/O設備之間交換信息的部件,可以視為一種具有特殊功能的處理器。它是從屬于CPU的一個專用處理器,CPU不直接參與管理,故提高了CPU的資源利用率

    具有I/O處理機的階段

    輸入輸出系統發展到第四階段,出現了I/O處理機。I/O處理機又稱為外圍處理機,它獨立于主機工作,既可以完成I/O通道要完成的I/O控制,又完成格式處理,糾錯等操作。具有I/O處理機的輸出系統與CPU工作的并行度更高,這說明I.O系統對主機來說具有更大的獨立性。

    小結

    我們可以看到數據傳輸進化的目標是一直在減少CPU占有,提高CPU的資源利用率。

    數據拷貝

    先介紹一下今天我們的需求,在磁盤中有個文件,現在需要通過網絡傳輸出去。 如果是你應該怎么做?通過上面的一些介紹,相信你心中應該有些想法了吧。

    傳統拷貝

    如果我們用Java代碼實現的話用我們會有如下的的實現:偽代碼參考如下:

    public static void main(String[] args) { Socket socket = null; File file = new File("test.file"); byte[] b = new byte[(int) file.length()]; try { InputStream in = new FileInputStream(file); readFully(in, b); socket.getOutputStream().write(b); } catch (Exception e) { } } private static boolean readFully(InputStream in, byte[] b) { int size = b.length; int offset = 0; int len; for (; size > 0;) { try { len = in.read(b, offset, size); if (len == -1) { return false; } offset += len; size -= len; } catch (Exception ex) { return false; } } return true; }

    這是我們傳統的拷貝方式具體的數據流轉圖如下,PS:這里不考慮Java中傳輸數據時需要先將堆中的數據拷貝到直接內存中。?

    可以看見我們總管需要經歷四個階段,2次DMA,2次CPU中斷,總共四次拷貝,有四次上下文切換,并且會占用兩次CPU。

  • CPU發指令給I/O設備的DMA,由DMA將我們磁盤中的數據傳輸到內核空間的內核buffer。
  • 第二階段觸發我們的CPU中斷,CPU開始將將數據從kernel buffer拷貝至我們的應用緩存
  • CPU將數據從應用緩存拷貝到內核中的socket buffer.
  • DMA將數據從socket buffer中的數據拷貝到網卡緩存。
  • 優點:開發成本低,適合一些對性能要求不高的,比如一些什么管理系統這種我覺得就應該夠了

    缺點:多次上下文切換,占用多次CPU,性能比較低。

    sendFile實現零拷貝

    上面是零拷貝呢?在wiki中的定位:通常是指計算機在網絡上發送文件時,不需要將文件內容拷貝到用戶空間(User Space)而直接在內核空間(Kernel Space)中傳輸到網絡的方式。

    在java NIO中FileChannal.transferTo()實現了操作系統的sendFile,我們可以同下面偽代碼完成上面需求:

    public static void main(String[] args) { SocketChannel socketChannel = SocketChannel.open(); FileChannel fileChannel = new FileInputStream("test").getChannel(); fileChannel.transferTo(0,fileChannel.size(),socketChannel); }

    我們通過java.nio中的channel替代了我們上面的socket和fileInputStream,從而完成了我們的零拷貝。

    上面具體過程如下:

  • 調用sendfie(),CPU下發指令叫DMA將磁盤數據拷貝到內核buffer中。
  • DMA拷貝完成發出中斷請求,進行CPU拷貝,拷貝到socket buffer中。sendFile調用完成返回。 3.DMA將socket buffer拷貝至網卡buffer。
  • 可以看見我們根本沒有把數據復制到我們的應用緩存中,所以這種方式就是零拷貝。但是這種方式依然很蛋疼,雖然減少到了只有三次數據拷貝,但是還是需要CPU中斷復制數據。為啥呢?因為DMA需要知道內存地址我才能發送數據啊。所以在Linux2.4內核中做了改進,將Kernel buffer中對應的數據描述信息(內存地址,偏移量)記錄到相應的socket緩沖區當中。 最終形成了下面的過程: ![](https://user-gold-cdn.xitu.io/2018/8/2/164f669eaa58b005?w=1094&h=862&f=png&s=88495

    這種方式讓CPU全程不參與拷貝,因此效率是最好的。

    在第三方開源框架中Netty中都有類似的代碼,大家如果感興趣可以下來自行搜索。

    mmap映射

    上面我們提到了零拷貝的實現,但是我們只能將數據原封不動的發給用戶,并不能自己使用。于是Linux提供的一種訪問磁盤文件的特殊方式,可以將內存中某塊地址空間和我們要指定的磁盤文件相關聯,從而把我們對這塊內存的訪問轉換為對磁盤文件的訪問,這種技術稱為內存映射(Memory Mapping)。 我們通過這種技術將文件直接映射到用戶態的內存地址,這樣對文件的操作不再是write/read,而是直接對內存地址的操作。

    在Java中依靠MappedByteBuffer進行mmap映射,具體的MappedByteBuffer可以詳情參照這篇文章:https://www.jianshu.com/p/f90866dcbffc?。

    最后

    自此,零拷貝的神秘面紗也被揭蓋,零拷貝只是為了減少CPU的占用,讓CPU做更多真正業務上的事。通過這篇文章,大家可以自己下來看看Netty是怎么做零拷貝的相信將會有更加深刻的印象。

    轉載于:https://www.cnblogs.com/davidwang456/articles/10818641.html

    總結

    以上是生活随笔為你收集整理的深入剖析神秘的“零拷贝”的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。