Java 两种zero-copy零拷贝技术mmap和sendfile的介绍
詳細介紹了兩種zero-copy零拷貝技術mmap和sendfile的概念和基本原理。
文章目錄
- 1 標準IO
- 2 零拷貝
- 2.1 sendfile調用
- 2.1 mmap調用
- 2.2 MQ中的應用
 
1 標準IO
很多軟件是基于server-client模式的,最常見的下載功能需要從Server端的磁盤中將文件通過網絡發送到客戶端中去。如果采用傳統標準IO的方式(基于數據拷貝),那么需要如下步驟:
傳統標準IO通過網絡傳輸數據,需要進行如下調用:
buffer = File.read Socket.send(buffer)總共需要四步:
可以發現,完成一次讀寫,需要4此上下文切換、2次DMA數據拷貝、兩次CPU數據拷貝,實際上,如果僅僅是數據傳輸,那么數據根本不需要經過這么多次的拷貝。
DMA:Direct Memory Access ,它可以獨立地直接讀寫系統內存,不需要 CPU 介入,像顯卡、網卡之類都會用DMA。
2 零拷貝
零拷貝(Zero-copy)技術是指計算機執行操作時,CPU不需要先將數據從某處內存復制到另一個特定區域。這種技術通常用于通過網絡傳輸數據時節省CPU周期和內存帶寬。零拷貝技術可以減少數據拷貝和共享總線操作的次數,消除傳輸數據在存儲器之間不必要的中間拷貝次數,從而有效地提高數據傳輸效率。而且,零拷貝技術減少了用戶進程地址空間和內核地址空間之間因為上下文切換而帶來的開銷。
常見的零拷貝技術分類:
下面介紹數據傳輸不經過用戶空間的零拷貝技術:mmap和sendfile,這也是Netty、Kafka、RocketMQ等框架所使用的底層技術。
2.1 sendfile調用
Linux 在版本 2.1 中引入了 sendfile() 這個系統調用,sendfile()是一種零拷貝的實現。Java對sendfile的支持就是NIO中的FileChannel.transferTo()或者transferFrom()。
 
使用sendfile進行網絡數據傳輸流程為:
可以看到整個流程,減少了一次CPU Copy,減少了兩次的上下文切換,相比于傳統IO確實提升了性能。
但是,數據仍舊需要一次從Page Cache到Socket Cache的CPU Copy,這個Copy能不能也去掉呢?
當然可以,Linux 2.4+ 版本之后,文件描述符結果被改變,借助DMA Gather(帶有收集功能的DMA),sendfile()再次減少了一次 Copy 操作,變成了真正的零拷貝(沒有CPU Copy)。
此時整個步驟變為:
sendfile + DMA Gather流程如下:
sendfile + DMA Gather,使得整個傳輸只需要兩次上下文切換,數據只需要兩次DMA Copy,降低了上下文切換和數據拷貝帶來的開銷,極大的提升了數據傳輸的效率,沒有CUP拷貝,是真正的零拷貝。
但是,sendfile調用有一個缺點,那就是無法在sendfile調用過程中修改數據,因此sendfile()只是適用于應用程序地址空間不需要對所訪問數據進行處理的和修改情況,常見的就是文件傳輸,或者MQ消費消息的獲取,如果想要在傳輸過程中修改數據,可以使用mmap系統調用。
mmap調用是一個比sendfile調用昂貴但優于傳統I/O的零拷貝實現方式,而mmap調用則可以在中途直接修改Page Cache中的數據,這也是mmap零拷貝的優點。
2.1 mmap調用
mmap(Memory Mapped Files)是一種零拷貝技術,學名內存映射文件,Java中的實現就是MappedByteBuffer,通過channel#map方法得到。
mmap將一個文件(或者文件的一部分)映射到進程的地址空間,實現文件磁盤地址和進程虛擬地址空間中一段虛擬地址的一一對映關系。注意這時候沒有分配和映射到具體的物理內存空間,而是到第一次加載這個文件的時候,通過MMU把之前虛擬地址換算成物理地址,把文件加載進物理內存——內核空間的Page Cache中。
實現這樣的映射關系后,進程就可以采用指針的方式讀寫操作這一段內存,而系統會自動回寫臟頁面到對應的文件磁盤上,即完成了對文件的操作而不必再調用 read,write 等系統調用函數。相反,內核空間對這段區域的修改也直接反映用戶空間,從而可以實現不同進程間的文件共享。
簡單的說,使用mmap之后,數據無需拷貝到用戶空間中,應用程序可以直接操作Page Cache中的數據。
mmap()代替read()調用之后的數據發送流程為:
buf = mmap(file, len); write(sockfd, buf, len);使用mmap技術之后,數據流轉圖如下:
此時整個步驟變為:
這種mmap+write的方式相比于傳統IO少了一次CPU Copy,從而極大地提高了效率。雖然性能弱于sendfile零拷貝,但其好處是可以在中途修改內存中的數據之后再傳輸。
另外,當應用程序往 mmap 輸出數據時,此時就直接輸出到了內核態的緩沖區數據,如果此時輸出設備是磁盤的話,不會立即寫磁盤,linux系統下通常會間隔是30秒由操作系統自動落盤,也可手動調用fsync()函數讓其立即落盤,實現真正的持久化。
2.2 MQ中的應用
對于Kafka來說:
對于rocketMQ來說,如論是消息存儲還是消費,都是采用mmap的方式,并且通過預熱來減少大文件 mmap 因為缺頁中斷產生的性能問題。
參考資料:
如有需要交流,或者文章有誤,請直接留言。另外希望點贊、收藏、關注,我將不間斷更新各種Java學習博客!
總結
以上是生活随笔為你收集整理的Java 两种zero-copy零拷贝技术mmap和sendfile的介绍的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 个人的一些思考
- 下一篇: 民族企业家周景川:凡事勤则易,凡事惰则难
