架構之零拷貝

https://blog.51cto.com/12182612/2424692?source=dra

傳統讀操作

阿里P7二面:聊聊零拷貝的原理

 

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

1、應用程序發起讀數據操作,JVM會發起read()系統調用。

2、這時操作系統OS會進行一次上下文切換(把用戶空間切換到內核空間)

3、通過磁盤控制器把數據copy到內核緩衝區中,這裏的就發生了一次DMA Copy

4、然後內核將數據copy到用戶空間的應用緩衝區中,發生了一次CPU Copy

5、read調用返回後,會再進行一次上下文切換(把內核空間切換到用戶空間)

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

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

傳統寫操作

阿里P7二面:聊聊零拷貝的原理

 

上圖是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、虛擬內存空間可以遠遠大於物理內存空間

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

阿里P7二面:聊聊零拷貝的原理

 

mmap+write方式

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

阿里P7二面:聊聊零拷貝的原理

 

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

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

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

sendfile方式

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

mmap();
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中,這樣就不需要複製(其實本質就是和虛擬內存的解決方法思路一樣,就是內存地址的記錄),如圖:

阿里P7二面:聊聊零拷貝的原理

 

JAVA零拷貝

java nio實現零拷貝,JAVA提供了一下方法類:

1、MappedByteBuffer
2、DirectByteBuffer
3、FileChannel.transferTo

具體如何使用,小夥伴們上網自行查閱。

總結

零拷貝技術在很多中間件中,都有利用;如:Kafka,Spark、RocketMQ等,這個在網絡傳輸數據時,能夠提升速度,提升系統性能、吞吐量。小夥伴們不一定會編寫,可以先了解基本原理就行。很多好的中間件產品都需要了解一些計算機原理方面的知識,纔會更深入的理解。

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