淺談mmap介紹

相關背景知識

  • 說到mmap的使用,我們首先要了解一下進程的虛擬進程地址空間的概念。Linux上為了作進程隔離,每個進程都運行在自己的單獨的虛擬進程空間,同時物理機上內存有限,每個進程使用虛擬內存地址來隔離又共享物理內存。我們平時在代碼裡獲取的地址就是虛擬地址;
  • 放一張進程虛擬地址空間草圖,網上也可以很容易找到更精美的
淺談mmap介紹

  1. 我們在程序中申請內存的操作,實際上只是在進程地址空間相應部分申請了一段虛擬地址,當實際對這段虛擬地址進行讀寫操作時,才會分配真正的物理內存;
  2. 通常x86 Linux採用段頁式的內存管理模式,這塊不具體展開,簡單來說就是CPU訪問的邏輯地址,然後經過分段機制轉換成線性地址(你可以簡單理解成等價於上面說的虛擬地址),再經過分頁機制轉換成物理地址,第一次訪問的時候由於實現物理地址還沒有分配,會產生缺頁中斷來分配物理地址,用它來填充對應的頁表項;
  3. 通過 read 系統調用來讀取磁盤上的文件時,文件內容會先被讀到內存的page inode 部分,然後再從page cache中拷貝到應用層的讀緩存buffer中;對於打開的文件,內核都會在內存中維護一個inode結構體(對於同一個文件,即使被open多次,內核也僅維護這一個inode),其有一個成員是 struct address_space *i_mapping , 它用來維護這個文件被讀取的所有部分在內存中的緩存,其使用 xarray (全新封裝了基數樹的操作)來存儲這個物理頁(struct page), 如下圖:
淺談mmap介紹

mmap簡介

  • 先看原型: void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)
  • 功能:
  1. 分配一塊新的連續的進程虛擬地址段(對應內核中的結構體就是 vm_area_struct)並返回其起始地址,如果給定了第一個參數,就優先從這個地址開始分配進程虛擬地址;
  2. 如果提供了fd文件句枘,則映射文件內容到進程虛擬地址;
  3. mmap的參數較多,其中prot和flags的可選項也比較多,具體大家可以使用 man命令查看;

mmap的幾種典型應用

  • 不同進程(可以是非父子進程)間共享映射
  1. 這種情況需要藉助磁盤文件,實際上是共享這個磁盤文件,將這個磁盤文件映射到各自的進程虛擬地址空間,但是其虛擬地址空間分頁轉換後其頁表項對應的物理內存是相同的;
  2. 典型用法是需提供一個打開的文件句柄,使用 MAP_SHARED flag
  • 非子進程間通訊
  1. 父進程使用 fork創建子進程,父子進程間可以使用mmap來通讀;
  2. 典型用法是無需提供打開的文件句柄, 使用 MAP_SHARED | MAP_ANONYMOUS flag,
  • 進程通過 mmap 來讀寫文件
  1. 從上面 相關背景知識 一節可知使用 read 系統調用讀文件時,數據需經過 磁盤拷貝到page cache, page cache再拷貝到應用層緩存bufffer, 這兩個數據拷貝;
  2. 使用 mmap 時,磁盤數據也是先讀到page cache中,然後會將mmap返回的虛擬地址最終對應的頁項表內容設定為和前面的page chache相同的物理頁, 這樣一來就免去了第二次的數據拷貝;
  3. 用個示意圖來說明一下:
淺談mmap介紹

  • 用作glibc中malloc申請內存
  1. 通常我們都說是通過調用 malloc 來申請堆上內存,但實際上其內部實現使用了 brk 和 mmap 兩種系統調用,當申請的內存大於128K時,使用 mmap
  2. 典型用法是無需提供打開的文件句柄, 使用 MAP_PRIVATE | MAP_ANONYMOUS flag
  • mmap的寫時拷貝
  1. 如果我們在調用mmap時提供一個打開的文件句兩,但使用 MAP_PRIVATE的flags, 那這時對其的寫操作並不能真正修改對應的磁盤文件,它會作寫時拷貝,退化成匿名映射

mmap作磁盤文件映射時的特別說明

  • mmap映射的虛擬地址長度(即mmap的第二個參數)需要對齊到物理頁大小,在32位系統上通常是4K, 這一特點會導致一些有趣的事情發生,我們來看一下:假如一個文件的大小是5000Byte, 剛好比4K大一些,我們用mmap來從文件開始的位置來映射它,mmap的第二個參數給5000, 因為需要頁面對齊,實現映射的虛擬地址長度將是兩個4k,即8192, 8192 - 5000 = 3192的部分用0填充,也是可以被訪問到;
  • 如果用mmap映射某個文件時,這個文件大小為0, 不會分配任何的物理內存,也不能作任何的讀寫訪問;當向文件中寫入數據後,通過mmap返回的虛擬地址可以訪問這部分文件內容;

mmap與內存換入換出

  • 由前面的介紹我們知道mmap不管是映射磁盤文件,還是作匿名映射,最終都會分配物理內存頁,因此這個物理內存頁在內存緊張時就有備換出的可能,當然mmap提供了MAP_LOCKED,可以鎖定內存不被換出,我們不考慮這種情況;
  • 如果使用mmap映射的是磁盤文件,其存在物理頁的內容會被清空,pte將記錄這種情況,再次需要訪問時,會重新讀取磁盤文件,緩存在page cache中;
  • 如果使用mmap作匿名映射,沒有相關聯的磁盤文件(或者使用MAP_PRIVATE方式映射磁盤文件),發生內存換出時,將被交換到swap中,swap實際上也對應著磁盤塊,最終也是寫在磁盤上;


分享到:


相關文章: