天天看點

零拷貝你知道嗎?你聽說過嗎?

面試官: 零拷貝知道嗎?

   ..

面試官:MMAP知道嗎?

  不知道..

 面試官: 回去等結果吧!

  複制一個檔案的内容到另外一個檔案,有極好的處理方式,也有極差的處理方式。不過如果一般我們使用的就是極差的方式。

  來看看應該如何做這個拷貝的優化,這需要用到linux核心的使用者态和核心态切換的概念,以及一點點核心的知識。

看看極差情況下的代碼是如何寫

File file = new File("test.txt");
RandomAccessFile raf = new RandomAccessFile(file, "rw");

byte[] arr = new byte[(int) file.length()];
raf.read(arr);

Socket socket = new ServerSocket(8080).accept();
socket.getOutputStream().write(arr);
           

看看極差的傳統IO的原理圖

零拷貝你知道嗎?你聽說過嗎?
  1.  先說一下linux核心的使用者态和核心态轉換的問題。這實際上是linux核心的一種設計方式。linux的設計者為了保護系統的安全性,把核心的内容封裝在了linux核心。然後對外提供了調用的api,也就是提供了調用的方法,而使用這些方法的正是使用者态,我們的程式實際上是和使用者态互動的,我們把指令交給使用者态,然後使用者态去調用核心态真正的執行。是以這就設計到了使用者态和核心态轉換的問題,雖然這個花費的時間是極短的,但是也是需要注意到的點。 結合上邊的圖,其實紅綠色的圈,就是代表一次狀态的切換。我們一個傳統的IO,實際上要經曆四次的狀态切換。
  2. 關于拷貝。上邊說了狀态的切換,實際上還要知道兩個空間。使用者态對應的使用者空間,核心态對應的是核心空間。它們都是獨立的,不互相通路。是以最差的情況下,就是上圖,經曆一次 DMA的直接記憶體的拷貝,把内容拷貝到了核心空間,然後走了一次CPU拷貝,把内容從核心空間拷貝到了使用者空間。在使用者空間上的内容,是我們的寫的程式可以操作的内容,經由我們的程式操作完了以後,由經CPU拷貝到了核心态,然後又經過DMA直接拷貝到了協定棧。
  3. MDA:直接記憶體拷貝,不經過不占用CPU。

MMAP,核心的進步,節省了一次CPU的拷貝 

   mmap實際上就是通過開辟共享空間的方式,可以讓使用者空間和核心空間都能通路到。這樣就不用從核心态經由CPU拷貝到使用者态去操作了,因為是共享的,是以我們的程式可以直接的操作。如下圖我用紅框圈起來的,就可以看做是MMAP空間。

   這是linux核心給我們帶來的提升。

零拷貝你知道嗎?你聽說過嗎?

sendFile 優化 - 零拷貝的真正實作

    這也是基于linux核心的更新,為我們真正的去掉了最後的一次CPU拷貝,達到了零拷貝的效果。但是所謂的領拷貝,它是指沒有了CPU的拷貝。從圖上還可以看到,從記憶體夠直接拷貝到核心空間的操作還是有的,但是它不走CPU。

 Linux2.1

 版本提供了 

sendFile

 函數,其基本原理如下:資料根本不經過使用者态,直接從核心緩沖區進入到 

SocketBuffer

,同時,由于和使用者态完全無關,就減少了一次上下文切換

零拷貝你知道嗎?你聽說過嗎?