Linux傳統上的資料。現在要把我從磁碟發到網卡,需要經過以下步驟:
#如上圖:作業系統把記憶體分為了核心空間和使用者空間。首先位於使用者空間的應用程式使用發起資料讀取操作,例如JVM發起read()系統呼叫。這時候作業系統會進行一次
上下文切換:從使用者空間切換到核心空間。最後進行上下文切換,再換回使用者空間的上下文。然後核心空間通知磁碟,核心把我從磁碟copy到核心緩衝區。這個過程是由一個叫做「DMA(Direct memory access)」的硬體來做的,所以不需要CPU的參與。
然後核心把我從核心緩衝區copy到應用程式緩衝區,這裡需要CPU的參與。
相關學習推薦:Java影片教學
#寫操作與讀取操作類似,只是方向相反而已,仍然需要兩次上下文切換和兩次資料的copy。我可能會被寫到磁碟,也可能會被寫到網卡。
記憶體映射
從上面的過程可以看到,如果想把我從磁碟發送到網卡,需要總共4次上下文切換和4次copy操作。我被作業系統在核心空間和使用者空間之間來回複製,但其實我在這段期間什麼也沒做,什麼也沒有變化,就是複製而已,所以這個IO模型太浪費作業系統資源了,我被複製這麼多次,身心疲憊。而且作業系統的資源是非常寶貴滴~虛擬位址取代實體位址
,這樣做可以讓多個虛擬記憶體只想同一個實體位址,虛擬記憶體的空間可以遠大於實體記憶體的空間。所以為了解決這個問題,聰明的Linux開發者們寫了一些新的系統呼叫來做這個事。主要有兩種方式:mmap write
mmap write
mmap()
系統呼叫首先會使用DMA copy的方式將我從磁碟讀取到核心緩衝區,然後透過記憶體映射的方式,使用戶緩衝區和核心讀取緩衝區的記憶體位址為同一記憶體位址,也就是說,不需要CPU再將我從核心讀取緩衝區複製到用戶緩衝區啦!
網路發送緩衝區(socket buffer)
,然後透過DMA的方式將我傳入到網卡驅動程式(或磁碟)中準備發送。 ###############mmap write的方式讀寫資料總共需要兩次系統調用,4次上下文切換,2次DMA Copy和1次CPU Copy。 ############sendfile######sendfile也是一個系統調用,它其實本質上就是把上述兩個系統調用的函數合起來,變成了一個調用。這樣做的好處是,作業系統只需要2次上下文切換了,減少了2次上下文切換的開銷。 ###Linux2.4核心對sendfile進行了優化,提供了了解Linux 和 Java 的零拷貝操作,這個操作可以把上圖中的最後一次CPU copy去掉,原理就是不複製數據,而是把資料在之前的核心緩衝區(比如圖中的案例是Read Buffer)的記憶體位址、偏移記錄傳送給目標核心緩衝區(比如圖中案例的Socket Buffer),這樣在最後的DMA copy階段就可以拿著這個指標直接去找資料copy了。
Linux的零拷貝確實能夠節省一些作業系統的資源。所以Java的NIO為了支援零拷貝,提供了一些類別:
在之前的《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
#來源:慕課網
以上內容來自幕課網
零拷貝,是從操作系統的角度來說的。因為內核緩衝區之間,沒有資料是重複的(只有 kernel buffer 有一份資料)。
零拷貝不僅帶來更少的資料複製,還能帶來其他的效能優勢,例如更少的上下文切換,更少的CPU 快取偽共享以及無CPU校驗和計算。
#mmap 適合小資料量讀寫,sendFile 適合大檔案傳輸。
mmap 需要 4 次上下文切換,3 次資料拷貝;sendFile 需要 3 次上下文切換,最少 2 次資料拷貝。
sendFile 可以利用 DMA 方式,減少 CPU 拷貝,mmap 則不能(必須從核心拷貝到 Socket 緩衝區)。
以上是了解Linux 和 Java 的零拷貝的詳細內容。更多資訊請關注PHP中文網其他相關文章!