零拷貝(zero copy)技術你真的懂嗎?什麼時候需要用到內存映射?

Linux系統內存管理知識補充

Linux系統是虛擬內存系統,虛擬內存並不是真正的物理內存,而是虛擬的連續內存地址空間。虛擬內存又分為內核空間和用戶空間,內核空間是內核程序運行的地方,用戶空間是用戶進程代碼運行的地方,只有內核才能直接訪問物理內存併為用戶空間映射物理內存(MMU)。內核會為每個進程分配獨立的連續的虛擬內存空間,並且在需要的時候映射物理內存,為了完成內存映射,內核為每個進程都維護了一張頁表,記錄虛擬地址與物理地址的映射關係,這個頁表就是存在於MMU中;用戶進程訪問內存的時候,通過頁表把虛擬內存地址轉換為物理內存地址進而訪問數據;其實對於用戶進程而言,虛擬內存就是內存一般的存在(當作內存看待就好)。這樣的設計可以把用戶程序和系統程序分開,互不影響;內核可以對所有的用戶程序進行管理,比如限制內存濫用等。

虛擬內存的最小單位是頁,通常是4KB大小,所以虛擬內存會有很多很多的頁組成,當然也有大頁,顧名思義就是大的虛擬內存空間,比如12KB,2MB。虛擬內存和物理內存的映射都是等空間的,映射的物理內存是多大的,那麼佔用的虛擬內存差不多也是多大,都是4KB的整數倍。比如映射了一個1KB的內存空間,那麼也是佔用一頁4KB虛擬內存。

用戶進程在處於用戶態時,只能訪問用戶空間;只有進入內核態後,才可以訪問內核空間。雖然每個進程的地址空間都包含了內核空間,但這些內核空間映射的物理內存都是相同的,所以當進程切換到內核態後可以快速的訪問內核空間數據。

內核其實就是一段特殊的代碼程序,運行於內核空間,控制著計算機的CPU、IO、內存等,提供了一系列的系統接口供外部調用,通常叫做系統調用。只有線程或者進程處於內核態的時候才能進行系統調用,如果處於用戶態的話,是需要轉換為內核態才能訪問。其實就是權限不同,內核態(Ring0)擁有比用戶態(Ring3)更高的權限,擁有著訪問系統硬件資源的權限。

一般用戶線程或者進程是不需要切換到內核態運行的,除非:

1. 系統調用,其實系統調用本身就是中斷,但是軟件中斷,跟硬中斷不同。

2. 異常:如果當前進程運行在用戶態,如果這個時候發生了異常事件,就會觸發切換。

例如:缺頁異常。

3. 外設中斷:當外設完成用戶的請求時,會向CPU發送中斷信號。

比如讀取硬盤數據,除了IO屬於系統操作需要切換為內核態來獲取權限的原因外還要一原因是:

為了減少磁盤的IO操作,為了提高性能而考慮的,因為我們的程序訪問一般都帶有局部性,也就是所謂的局部性原理,即我們訪問了文件的某一段數據,那麼接下去很可能還會訪問接下去的一段數據,由於磁盤IO操作的速度比直接訪問內存慢了好幾個數量級,所以OS根據局部性原理會在一次 read()系統調用過程中預讀更多的文件數據緩存在內核IO緩衝區中,當繼續訪問的文件數據在緩衝區中時便直接拷貝數據到進程私有空間,避免了再次的低效率磁盤IO操作。

傳統IO發送文件

1. 用戶程序調用read,進入內核態,上下文切換由用戶空間切換為內核空間,由DMA(Direct Memory Access)加載文件數據到內核空間。

2. CPU把數據從內核空間複製到用戶空間,轉換為用戶態,上下文由內核空間切換為用戶空間。

3. 用戶程序調用write,再次進入內核態,CPU把數據從用戶空間複製到socket關聯的內核空間。

4. 最後通過DMA 將內核模式下的socket緩衝區中的數據複製到網卡設備中傳送,進而返回用戶空間進入用戶態。

sendfile零拷貝(<linux>

1. 用戶程序調用read,進入內核態,上下文切換由用戶空間切換為內核空間,由DMA(Direct Memory Access)加載文件數據到內核空間,第一步和傳統IO相同。

2. 在內核態下,CPU把數據從內核空間複製到socket關聯的內核空間。

3. 最後通過DMA 將內核模式下的socket緩衝區中的數據複製到網卡設備中傳送,進而返回用戶空間進入用戶態,最後一步也是和傳統IO相同。

與傳統IO相比,缺少了把數據從內核空間複製到用戶空間,再由用戶空間複製到內核空間,比原來缺少了一次CPU複製(複製3次,CPU參與複製一次),少了兩次上下文切換(兩次)。

**從內核空間角度來看,其實已經是“ZERO COPY”了,因為沒有往用戶空間複製的操作。**

sendfile零拷貝(>=Linux 2.4)

1. 用戶程序調用read,進入內核態,上下文切換由用戶空間切換為內核空間,由DMA(Direct Memory Access)加載文件數據到內核空間,第一步和傳統IO相同。

2. 在內核態下,描述符(包含了數據的位置和長度等信息)追加到socket關聯的緩衝區中,並沒有進行數據的拷貝。

3. 最後DMA根據提供的位置和偏移量信息直接將內核空間緩衝區中的數據拷貝到協議引擎上進而返回用戶空間進入用戶態。

**這次優化點在於沒有CPU參與複製,兩次DMA數據複製,不過還是兩次上下文切換。**

# 通過mmap實現的零拷貝(常用來處理大文件)

當進行mmap系統調用的時候,將文件的內容的全部或一部分直接映射到進程的地址空間(虛擬內存),映射完成後,進程可以像訪問普通內存一樣做其他的操作,mmap並不分配物理地址空間,它只是佔有進程的虛擬地址空間。

當進程訪問內核中的緩衝區時候,並沒有實際拷貝數據,這時MMU在地址映射表中是無法找到與ptr相對應的物理地址的,也就是MMU失敗,就會觸發缺頁中斷。內核將文件的這一頁數據讀入到內核高速緩衝區中,並更新用戶進程的頁表,使頁表指向內核緩衝中的這一頁,實現了用戶空間和內核空間數據的直接交換,可以看待為內核空間和用戶空間共享的一段物理內存。

Java調用零拷貝

<code>\t\tFileInputStream input = new FileInputStream("1.txt");
\t\tFileChannel channel = input.getChannel();
\t\tFileOutputStream out = new FileOutputStream("2.txt");
\t\tchannel.transferTo(0, channel.size(), out.getChannel());/<code>

上面這種方式其實調用的是Linux系統的sendfile系統指令,無論什麼語言代碼實現的零拷貝其實調用的都是操作系統本身提供的系統指令,只是做了封裝而已。

<code>\t\tFileInputStream input = new FileInputStream("1.txt");
\t\tFileChannel channel = input.getChannel();
\t\tMappedByteBuffer mappedBuffer = channel.map(MapMode.READ_ONLY, 0, channel.size());
\t\tSystem.out.println(Charset.forName("utf-8").decode(mappedBuffer).toString());/<code>

上面這種方式其實調用的是Linux系統的mmap系統指令;在讀取大文件的時候用這種方法映射大文件的一部分到內存空間,比較方便快捷。

<code>\t\t//mmap寫數據
\t\tInstant now = Instant.now();
\t\tRandomAccessFile outFile = new RandomAccessFile("1.txt","rw");
\t\tFileChannel channel = outFile.getChannel();
\t\tlong size = 1024*1024*60;
\t\tMappedByteBuffer mappedBuffer = channel.map(MapMode.READ_WRITE, 0, size);
\t\tfor(int i=0;i<1000000;i++) {
\t\t\tmappedBuffer.put("11111111111111111111111111111111111111111111111111111111111\\n".getBytes());
\t\t}
\t\tSystem.out.println(ChronoUnit.MILLIS.between(now, Instant.now()));
\t\t//fileOutputStream寫數據
\t\tInstant nowStream = Instant.now();
\t\tFileOutputStream outStream = new FileOutputStream("2.txt");
\t\tfor(int i=0;i<1000000;i++) {
\t\t\toutStream.write("11111111111111111111111111111111111111111111111111111111111\\n".getBytes());
\t\t}
\t\tSystem.out.println(ChronoUnit.MILLIS.between(nowStream, Instant.now()));/<code>
<code>118
9130/<code>

通過上面的測試可以看出在頻繁的寫入文件操作上mmap佔有很多大的優勢,數量級的優勢。但是把上例的循環次數改為50的話,mmap就不佔優勢了,因為在映射的時候需要新開闢內存空間,這個耗時相對於極少量的寫操作而言顯得佔比重就大了。



分享到:


相關文章: