這期內容當中小編將會給大家帶來有關Linux和Java使用零拷貝的方法,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
為東鄉族等地區用戶提供了全套網頁設計制作服務,及東鄉族網站建設行業解決方案。主營業務為成都網站建設、網站設計、東鄉族網站設計,以傳統方式定制建設網站,并提供域名空間備案等一條龍服務,秉承以專業、用心的態度為用戶提供真誠的服務。我們深信只要達到每一位用戶的要求,就會得到認可,從而選擇與我們長期合作。這樣,我們也可以走得更遠!
Linux傳統IO
大家好,我是一段躺在Linux磁盤上的數據?,F在要把我從磁盤發到網卡,需要經過以下步驟:
如上圖:操作系統把內存分為了內核空間和用戶空間。首先位于用戶空間的應用程序使用發起數據讀操作,比如JVM發起read()
系統調用。這個時候操作系統會進行一次上下文切換:從用戶空間切換到內核空間。
然后內核空間通知磁盤,內核把我從磁盤copy到內核緩沖區。這個過程是由一個叫“DMA(Direct memory access)”的硬件來做的,所以不需要CPU的參與。
然后內核把我從內核緩沖區copy到應用程序緩沖區,這里需要CPU的參與。
最后進行上下文切換,又換回到用戶空間的上下文。
整個讀操作的過程需要兩次上下文切換和兩次copy。
寫操作與讀操作類似,只是方向相反而已,仍然需要兩次上下文切換和兩次數據的copy。我可能會被寫到磁盤,也可能會被寫到網卡。
從上面的過程可以看到,如果想把我從磁盤發送到網卡,需要總共4次上下文切換和4次copy操作。我被操作系統在內核空間和用戶空間之間來回復制,但其實我在這期間什么也沒有做,什么也沒有變化,就是復制而已,所以這個IO模型太浪費操作系統資源了,我被復制這么多次,身心疲憊。而且操作系統的資源是非常寶貴滴~
現在主流的操作系統都使用了虛擬內存。簡單來說,就是用虛擬地址取代物理地址,這樣做可以讓多個虛擬內存只想同一個物理地址,虛擬內存的空間可以遠遠大于物理內存的空間。
那如果操作系統能夠把用戶空間的應用程序緩沖區和內核空間的內核緩沖區映射到同一個物理地址,那豈不是就少了很多復制的過程?如下圖:
所以為了解決這個問題,聰明的Linux開發者們寫了一些新的系統調用來做這個事。主要有兩種方式:
mmap()
系統調用首先會使用DMA copy的方式將我從磁盤讀取到內核緩沖區,然后通過內存映射的方式,使用戶緩沖區和內核讀緩沖區的內存地址為同一內存地址,也就是說,不需要CPU再將我從內核讀緩沖區復制到用戶緩沖區啦!
當使用write()
系統調用的時候,CPU將我從內核緩沖區(等同于用戶緩沖區)直接寫入到需要發送的內核緩沖區,比如網絡發送緩沖區(socket buffer),然后通過DMA的方式將我傳入到網卡驅動程序(或磁盤)中準備發送。
mmap + write的方式讀寫數據總共需要兩次系統調用,4次上下文切換,2次DMA Copy和1次CPU Copy。
sendfile也是一個系統調用,它其實本質上就是把上述兩個系統調用的功能合起來,變成了一個調用。這樣做的好處是,操作系統只需要2次上下文切換了,減少了2次上下文切換的開銷。
Linux2.4內核對sendfile進行了優化,提供了gather操作,這個操作可以把上圖中的最后一次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);
零拷貝,是從操作系統的角度來說的。因為內核緩沖區之間,沒有數據是重復的(只有 kernel buffer 有一份數據)。
零拷貝不僅僅帶來更少的數據復制,還能帶來其他的性能優勢,例如更少的上下文切換,更少的 CPU 緩存偽共享以及無 CPU 校驗和計算。
mmap 適合小數據量讀寫,sendFile 適合大文件傳輸。
mmap 需要 4 次上下文切換,3 次數據拷貝;sendFile 需要 3 次上下文切換,最少 2 次數據拷貝。
sendFile 可以利用 DMA 方式,減少 CPU 拷貝,mmap 則不能(必須從內核拷貝到 Socket 緩沖區)。
上述就是小編為大家分享的Linux和Java使用零拷貝的方法了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注創新互聯行業資訊頻道。
分享題目:Linux和Java使用零拷貝的方法
新聞來源:http://vcdvsql.cn/article28/phoejp.html
成都網站建設公司_創新互聯,為您提供網站維護、App設計、App開發、網站排名、網站制作、靜態網站
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯