零拷貝(mmap+write、sendfile)

傳統讀操作

JAVA用傳統方式進行讀操作時整體流程如上圖,具體如下:

1、應用程序發起讀數據操作,JVM會發起read()系統調用。
2、這時操作系統OS會進行   一次上下文切換(把用戶空間切換到內核空間)
3、通過   磁盤控制器把數據copy到內核緩衝區中,這裏的就發生了   一次DMACopy
4、然後內核將   數據copy到用戶空間的應用緩衝區中,   發生了一次CPU Copy
5、read調用返回後,會   再進行一次上下文切換(把內核空間切換到用戶空間)

我們看一下一個讀操作,發了2次上下文切換,和2次數據copy,一次是DMA Copy,一次是CPU Copy。

注意一點的是 內核從磁盤上面讀取數據 是    不消耗CPU時間的,是通過磁盤控制器完成;稱之爲DMA Copy。

傳統寫操作

上圖是JAVA傳統的寫操作,具體流程:

1、應用發起寫操作,OS進行   一次上下文切換(從用戶空間切換爲內核空間)
2、並且把數據copy到內核緩衝區Socket Buffer,   做了一次CPU Copy
3、內核空間再把數據copy到磁盤或其他存儲(網卡,進行網絡傳輸),   進行了DMA Copy
4、寫入結束後返回,又從   內核空間切換到用戶空間

傳統IO

我們可以看出傳統的IO讀寫操作,總共進行了4次上下文切換,4次Copy動作。我們可以看到數據在內核空間和應用空間之間來回複製,其實他們什麼都沒有做,就是複製而已,這個機制太浪費時間了,而且是浪費的CPU的時間

那我們能不能讓數據不要來回複製呢?零拷貝這個技術就是來解決這個問題。關於零拷貝提供了兩種解決方式:mmap+write方式、sendfile方式

 

 

虛擬內存

所有現代操作系統都使用虛擬內存,使用虛擬地址取代物理地址,這樣做的好處就是:

1、多個虛擬內存可以指向同一個物理地址
2、虛擬內存空間可以遠遠大於物理內存空間

我們利用第一條特性可以優化一下上面的設計思路,就是把內核空間和用戶空間的虛擬地址映射到同一個物理地址,這樣就不需要來回複製了,看圖:

mmap+write方式

使用mmap+write方式替換原來的傳統IO方式,就是利用了虛擬內存的特性,看圖

                                                            mmap讀流程

mmap寫流程

整體流程的核心區別就是,把數據讀取到內核緩衝區後,應用程序進行寫入操作時,直接是把內核的Read Buffer的數據複製到 Socket Buffer 以便進行寫入,這次內核之間的複製也是需要CPU參與的

注意:最後把Socket Buffer數據拷貝到很多地方,統稱protocol engine(協議引擎)

這個流程就少了一個CPU Copy,提升了IO的速度。不過發現上下文的切換還是4次,沒有減少,因爲還是要應用程序發起write操作。那能不能減少上下文切換呢?

sendfile方式

這種方式可以替換上面的mmap+write方式,如:

 
  1.  
    mmap();
  2.  
    write();

替換爲

sendfile();

這樣就減少了一次上下文切換,因爲少了一個應用程序發起write操作,直接發起sendfile操作。
到這裏就只有3次Copy,其中只有1次CPU Copy;3次上下文切換。那能不能把CPU Copy減少到沒有呢?

gather

Linux2.4內核進行了優化,提供了gather操作,這個操作可以把最後一次CPU Copy去除,什麼原理呢?就是在內核空間Read Buffer和Socket Buffer不做數據複製,而是將Read Buffer的內存地址、偏移量記錄到相應的Socket Buffer中,這樣就不需要複製(其實本質就是和虛擬內存的解決方法思路一樣,就是內存地址的記錄),如圖:

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章