mmap概述


mmap/munmap接口是用戶空間的最常用的一個系統調用接口,無論是在用戶程序中分配內存、讀寫大文件,鏈接動態庫文件,還是多進程間共享內存,都可以看到mmap/munmap的身影。mmap/munmap函數聲明如下:

#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
int munmap(void *addr, size_t length);
  • addr:用於指定映射到進程空間的起始地址,為了應用程序的可移植性,一般設置為NULL,讓內核來選擇一個合適的地址。
  • length:表示映射到進程地址空間的大小
  • prot:用於設置內核映射區域的讀寫屬性等。
  • flags:用於設置內存映射的屬性,例如共享映射、私有映射等。
  • fd:表示這個是一個文件映射,fd是打開文件的句柄。
  • offset:在文件映射時,表示文件的偏移量。

prot參數通常表示映射頁面的的讀寫權限,可以有如下參數組合:

  • PROT_EXEC:表示映射的頁面是可以執行的。
  • PROT_READ:表示映射的頁面是可以讀取的。
  • PROT_WRITE:表示映射的頁面是可以寫入的。
  • PROT_NONE:表示映射的頁面是不可訪問的。

flags參數也是一個重要的參數,有如下常見的參數:

  • MAP_SHARED:創建一個共享映射的區域。多個進程可以通過共享映射方式來映射一個文件,這樣其他進程也可以看到映射內容的改變,修改后的內容會同步到磁盤文件中。
  • MAP_PRIVATE:創建一個私有的寫時復制的映射。多個進程可以通過私有映射的方式來映射一個文件,這樣其他進程不會看到映射內容的改變,修改后的內容也不會同步到磁盤文件中。
  • MAP_ANONYMOUS:創建一個匿名映射,即沒有關聯到文件的映射。
  • MAP_FIXED:使用參數addr創建映射,如果內核無法映射指定地址addr,那么mmap會返回失敗,參數addr要求按頁對齊。如果addr和length指定的進程地址空間和已有的VMA區域重疊,那么內核會調用do_munmap()函數把這段重疊區域銷毀,然后重新映射新的內容。
  • MAP_POPULATE:對於文件映射來說,會提前預讀文件內容到映射區域,該特性只支持私用映射。

參數fd可以看出mmap映射是否和文件相關聯,因此Linux內核中映射可以分為匿名映射和文件映射。

  • 匿名映射:沒有映射對應的相關文件,這種映射的內存區域的內容會被初始化為0。
  • 文件映射:映射和實際文件相關聯,通常是把文件的內容映射到進程地址空間,這樣應用程序就可以像操作進程地址空間一樣讀寫文件。

最后根據文件關聯性和映射區域是否共享等屬性,又可以分為如下4種,見表2.1。

​ mmap映射類型

映射類型 映射類型
私有映射 共享映射
匿名映射 私有匿名映射-通常用於內存分配 共享匿名映射-通常用於進程間共享內存
文件映射 私有文件映射-通常用於加載動態庫 共享文件映射-通常用於內存映射IO,進程間通信
  1. 私有匿名映射

當使用參數fd=-1且flags=MAP_ANONYMOUS | MAP_PRIVATE時,創建的mmap映射是私有匿名映射。私有匿名映射最常見的用途是在glibc分配大塊內存中,當需要的分配的內存大於MMAP_THREASHOLD(128KB)時,glibc會默認使用mmap代替brk來分配內存。

  1. 共享匿名映射

當使用參數fd=-1且flags=MAP_ANONYMOUS | MAP_SHARED。在這種情況下,創建共享匿名映射。共享匿名映射讓相關進程共享一塊內存區域,通常用於父子進程的之間通信。

創建共享匿名映射有如下兩種方式:

(1)fd=-1且flags= MAP_ANONYMOUS|MAP_SHARED。在這種情況下,do_mmap_pgoff()->mmap()函數最終調用shmem_zero_setup()來打開一個"/dev/zero"特殊的設備文件。

(2)另外一個是直接打開"/dev/zero"設備文件,然后使用這個文件句柄來創建mmap。

  1. 私有文件映射

私有文件映射時flags的標志位被設置為MAP_PRIVATE,那么就會創建私有文件映射。

私有文件映射的最常用的場景是加載動態共享庫。

  1. 共享文件映射

創建文件映射時flags的標志位被設置為MAP_SHARED,那么就會創建共享文件映射。如果prot參數指定了PROT_WRITE,那么打開文件需要制定O_RDWR標志位。共享文件映射通常有如下場景:

(1)讀寫文件:

把文件內容映射到進程地址空間,同時對映射的內容做了修改,內核的回寫機制(writeback)最終會把修改的內容同步到磁盤中。

(2)進程間通信:

進程之間的進程地址空間相互隔離,一個進程不能訪問到另外一個進程的地址空間。如果多個進程都同時映射到一個相同的文件,就實現了多進程間的共享內存的通信。如果一個進程對映射內容做了修改,那么另外的進程是可以看到的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM