專題:Linux內存管理專題html
關鍵詞:文件映射、匿名映射、私有映射、共享映射數組
mmap/munmap是經常使用的一個系統調用,使用場景是:分配內存、讀寫大文件、鏈接動態庫文件、多進程間共享內存。 app
更詳細解讀參考《Linux內存管理 (9)mmap(補充)》。函數
mmap/munmap函數聲明以下:post
#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:表示映射到進程地址空間的大小。ui
prot:內存區域的讀/寫/執行屬性。this
flags:內存映射的屬性,共享、私有、匿名、文件等。atom
#define PROT_READ 0x1 /* page can be read */ #define PROT_WRITE 0x2 /* page can be written */ #define PROT_EXEC 0x4 /* page can be executed */ #define PROT_SEM 0x8 /* page may be used for atomic ops */ #define PROT_NONE 0x0 /* page can not be accessed */ #define PROT_GROWSDOWN 0x01000000 /* mprotect flag: extend change to start of growsdown vma */ #define PROT_GROWSUP 0x02000000 /* mprotect flag: extend change to end of growsup vma */
#define MAP_SHARED 0x01 /* Share changes */---------建立一個共享映射的區域,多個進程能夠映射到一個文件,掐進程能夠看到映射內容的改變,修改後內容會同步到磁盤中。 #define MAP_PRIVATE 0x02 /* Changes are private */--建立一個私有的寫時複製的映射,其餘進程看不到映射內容的改變,也不會同步到磁盤中。 #define MAP_TYPE 0x0f /* Mask for type of mapping */ #define MAP_FIXED 0x10 /* Interpret addr exactly */-使用指定的映射起始地址,若是有start和len參數指定的內存區重疊於現存的映射空間,重疊部分將會被丟棄。若是指定起始地址不可用,操做將會失敗。而且起始地址必須落在頁的邊界上。 #define MAP_ANONYMOUS 0x20 /* don't use a file */---匿名映射,映射區不與任何文件關聯。此時fd應設置爲-1。 #ifdef CONFIG_MMAP_ALLOW_UNINITIALIZED # define MAP_UNINITIALIZED 0x4000000 /* For anonymous mmap, memory could be uninitialized */ #else # define MAP_UNINITIALIZED 0x0 /* Don't support this flag */ #endif #define MAP_GROWSDOWN 0x0100 /* stack-like segment */--------------告訴內核VM系統,映射區能夠向下擴展。 #define MAP_DENYWRITE 0x0800 /* ETXTBSY */ #define MAP_EXECUTABLE 0x1000 /* mark it as an executable */ #define MAP_LOCKED 0x2000 /* pages are locked */-------------------鎖定映射區頁面,從而防止頁面被交換出內存。 #define MAP_NORESERVE 0x4000 /* don't check for reservations */ #define MAP_POPULATE 0x8000 /* populate (prefault) pagetables */---對文件映射來講,會提早預讀文件內容到映射區域,只支持私有映射。 #define MAP_NONBLOCK 0x10000 /* do not block on IO */--------------和MAP_POPULATE一塊兒使用時纔有意義。不執行預讀,只爲已存在與內存中的頁面創建頁表入口。 #define MAP_STACK 0x20000 /* give out an address that is best suited for process/thread stacks */ #define MAP_HUGETLB 0x40000 /* create a huge page mapping */
根據mmap是否映射到文件、是共享仍是私有映射,將映射類型分紅四類,使用場景以下:
場景 | 私有影射 | 共享映射 |
匿名映射 | 一般用於內存分配 fd=-1,flags=MAP_ANONYMOUS|MAP_PRIVATE |
一般用於進程間內存共享,經常使用於父子進程之間通訊。 FD=-1,flags=MAP_ANONYMOUS|MAP_SHARED |
文件映射 | 一般用於加載動態庫 flags=MAP_PRIVATE |
一般用於內存映射IO、進程間通訊、讀寫文件。 flags=MAP_SHARED |
用戶空間的mmap,在內核中的起點是mmap_pgoff。
問題1:兩次對相同地址執行mmap是否成功?
#include <stdio.h> #include <sys/mman.h> void main(void) { char *pmap1, *pmap2; pmap1 = (char *)mmap(0x20000000, 10240, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0); if(MAP_FAILED == pmap1) printf("pmap1 failed\n"); pmap2 = (char *)mmap(0x20000000, 1024, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0); if(MAP_FAILED == pmap2) printf("pmap1 failed\n"); }
在Ubuntu上執行strace ./mmap結果以下:
... mmap(0x20000000, 10240, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x20000000 mmap(0x20000000, 1024, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x20000000 ...
若是將第二個mmap的MAP_FIXED去掉呢?結果以下:
... mmap(0x20000000, 10240, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x20000000 mmap(0x20000000, 1024, PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fcb336e1000 ...
能夠看出若是映射區屬性包含MAP_FIXED,則會覆蓋原來區域;若是沒有MAP_FIXED,內核會找到一個區域。
unsigned long mmap_region(struct file *file, unsigned long addr, unsigned long len, vm_flags_t vm_flags, unsigned long pgoff) { ... munmap_back: if (find_vma_links(mm, addr, addr + len, &prev, &rb_link, &rb_parent)) { if (do_munmap(mm, addr, len))-----------------------------將衝突區域去映射 return -ENOMEM; goto munmap_back; } ... }
問題2:在一個播放系統中同時打開幾十個不一樣高清視頻文件,發現播放有些卡頓,打開文件使用的是mmap,分析緣由並解決。
mmap創建文件映射時,只創建了VMA,而沒有分配對應的頁面和創建映射關係。
播放時會不一樣發生缺頁異常去讀取文件內容,致使性能較差。
解決方法:1.對mmap映射後的地址用madvise(addr, len, MADV_SEQUENTIAL)。
2.經過"blockdev --setra"來增大內核默認預讀窗口,默認是128KB。