首頁  >  文章  >  Java  >  了解Linux 和 Java 的零拷貝

了解Linux 和 Java 的零拷貝

coldplay.xixi
coldplay.xixi轉載
2020-07-01 17:41:122579瀏覽

了解Linux 和 Java 的零拷貝

Linux傳統IO

Linux傳統IO

了解Linux 和 Java 的零拷貝

Linux傳統上的資料。現在要把我從磁碟發到網卡,需要經過以下步驟:

讀取操作

#如上圖:作業系統把記憶體分為了核心空間和使用者空間。首先位於使用者空間的應用程式使用發起資料讀取操作,例如JVM發起read()系統呼叫。這時候作業系統會進行一次

上下文切換
:從使用者空間切換到核心空間。

然後核心空間通知磁碟,核心把我從磁碟copy到核心緩衝區。這個過程是由一個叫做「DMA(Direct memory access)」的硬體來做的,所以不需要CPU的參與。
然後核心把我從核心緩衝區copy到應用程式緩衝區,這裡需要CPU的參與。

最後進行上下文切換,再換回使用者空間的上下文。

整個讀取操作的過程需要兩次上下文切換和兩次copy

相關學習推薦:了解Linux 和 Java 的零拷貝Java影片教學

寫操作

#寫操作與讀取操作類似,只是方向相反而已,仍然需要兩次上下文切換和兩次資料的copy。我可能會被寫到磁碟,也可能會被寫到網卡。

記憶體映射了解Linux 和 Java 的零拷貝

從上面的過程可以看到,如果想把我從磁碟發送到網卡,需要總共4次上下文切換和4次copy操作。我被作業系統在核心空間和使用者空間之間來回複製,但其實我在這段期間什麼也沒做,什麼也沒有變化,就是複製而已,所以這個IO模型太浪費作業系統資源了,我被複製這麼多次,身心疲憊。而且作業系統的資源是非常寶貴滴~

現在主流的作業系統都使用了虛擬記憶體

。簡單來說,就是用

虛擬位址取代實體位址

,這樣做可以讓多個虛擬記憶體只想同一個實體位址,虛擬記憶體的空間可以遠大於實體記憶體的空間。
  • 那如果作業系統能夠把使用者空間的應用程式緩衝區和核心空間的核心緩衝區映射到同一個實體位址,那豈不是就少了很多複製的過程?如下圖:

Linux零拷貝

所以為了解決這個問題,聰明的Linux開發者們寫了一些新的系統呼叫來做這個事。主要有兩種方式:mmap write

sendfile

mmap write

mmap + write mmap()

系統呼叫首先會使用DMA copy的方式將我從磁碟讀取到核心緩衝區,然後透過記憶體映射的方式,

使用戶緩衝區和核心讀取緩衝區的記憶體位址為同一記憶體位址,也就是說,不需要CPU再將我從核心讀取緩衝區複製到用戶緩衝區啦!

當使用write()

系統呼叫的時候,CPU將我從核心緩衝區(等同於使用者緩衝區)直接寫入到需要傳送的核心緩衝區,例如

網路發送緩衝區(socket buffer)

,然後透過DMA的方式將我傳入到網卡驅動程式(或磁碟)中準備發送。 ###############mmap write的方式讀寫資料總共需要兩次系統調用,4次上下文切換,2次DMA Copy和1次CPU Copy。 ############sendfile######sendfile也是一個系統調用,它其實本質上就是把上述兩個系統調用的函數合起來,變成了一個調用。這樣做的好處是,作業系統只需要2次上下文切換了,減少了2次上下文切換的開銷。 ###

了解Linux 和 Java 的零拷貝

Linux2.4核心對sendfile進行了優化,提供了了解Linux 和 Java 的零拷貝操作,這個操作可以把上圖中的最後一次CPU copy去掉,原理就是不複製數據,而是把資料在之前的核心緩衝區(比如圖中的案例是Read Buffer)的記憶體位址、偏移記錄傳送給目標核心緩衝區(比如圖中案例的Socket Buffer),這樣在最後的DMA copy階段就可以拿著這個指標直接去找資料copy了。

了解Linux 和 Java 的零拷貝

Java NIO使用零拷貝

Linux的零拷貝確實能夠節省一些作業系統的資源。所以Java的NIO為了支援零拷貝,提供了一些類別:

  • DirectByteBuffer
  • FileChannel

在之前的《Java NIO - Buffer》這篇文章裡大概介紹了DirectByteBuffer。 ByteBuffer主要有兩種實現,一種是DirectByteBuffer, 一種是HeapByteBuffer。

其中,DirectByteBuffer直接在堆外分配內存,底層是直接透過JNI調用作業系統的NIO系統調用,所以效能會比較高。而HeapByteBuffer是堆內內存,而且資料需要多一次拷貝,所以效能比較低。

FileChannel是Java NIO提供的複製檔案的類,可以把檔案複製到磁碟或網路等。

map方法其實就是採用了作業系統中的記憶體映射方式,將核心緩衝區的記憶體和使用者緩衝區的記憶體做了一個位址映射。

transferTo方法直接將目前頻道內容傳送到另一個頻道,也就是說這種方式不會有核心緩衝區到使用者緩衝區的讀寫問題。底層是sendfile系統呼叫。 transferFrom方法同理。

範例程式碼:

File file = new File("test.txt");RandomAccessFile raf = new RandomAccessFile(file, "rw");FileChannel fileChannel = raf.getChannel();SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("", 8080));// 直接使用了transferTo()进行通道间的数据传输fileChannel.transferTo(0, fileChannel.size(), socketChannel);

作者:公眾號_xy的技術圈

#連結:www.imooc.com/article/289550

#來源:慕課網

以上內容來自幕課網

零拷貝的再次理解

  1. 零拷貝,是從操作系統的角度來說的。因為內核緩衝區之間,沒有資料是重複的(只有 kernel buffer 有一份資料)。

  2. 零拷貝不僅帶來更少的資料複製,還能帶來其他的效能優勢,例如更少的上下文切換,更少的CPU 快取偽共享以及無CPU校驗和計算。

mmap和sendFile的差異

  1. #mmap 適合小資料量讀寫,sendFile 適合大檔案傳輸。

  2. mmap 需要 4 次上下文切換,3 次資料拷貝;sendFile 需要 3 次上下文切換,最少 2 次資料拷貝。

  3. sendFile 可以利用 DMA 方式,減少 CPU 拷貝,mmap 則不能(必須從核心拷貝到 Socket 緩衝區)。

#

以上是了解Linux 和 Java 的零拷貝的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:learnku.com。如有侵權,請聯絡admin@php.cn刪除