mmap概念
mmp是 將文件或設備直接映射到進程的虛擬內存空間 的一種機制,可實現程序像訪問內存一樣訪問文件,而不需要傳統的 read()/write()系統調用
文件內容被映射到進程的地址空間,讀寫文件就像操作內存一樣,操作系統負責自動同步內存和文件的修改(除非顯式禁用)。
好處:
- 文件 I/O 優化:避免頻繁的 read()/write(),提升性能,例如數據庫引擎(如 SQLite、LevelDB)用 mmap加速磁盤訪問
- 多個進程需要高效共享數據(如父子進程、獨立進程),比管道、消息隊列更快(直接內存訪問),Chrome 瀏覽器用 mmap共享多個標簽頁的內存,機器學習訓練時,多個進程共享模型參數
- 序運行時加載動態鏈接庫(.so/.dll)時,庫文件被映射到內存,按需加載(懶加載),節省內存
- sendfile:文件通過 mmap映射到內存,直接通過 DMA 發送到網卡,無需 CPU 參與拷貝
語法:函數聲明
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
參數 | 說明 |
---|---|
addr | 建議映射的起始地址(通常傳 NULL ,由系統自動分配) |
length | 映射區域的長度(字節) |
prot | 內存保護方式(PROT_READ , PROT_WRITE 等) |
flags | 映射類型(MAP_SHARED , MAP_PRIVATE ) |
fd | 文件描述符(通過 open 獲得) |
offset | 文件映射起始偏移(必須是頁大小的整數倍) |
相關常量:
// 保護方式
PROT_READ // 可讀
PROT_WRITE // 可寫
PROT_EXEC // 可執行
PROT_NONE // 不可訪問// 映射類型
MAP_SHARED // 修改會寫回文件,進程間共享
MAP_PRIVATE // 寫時復制,修改不寫回文件// 取消映射
int munmap(void *addr, size_t length); // 釋放映射
mmap寫文件
[root@prs31 01-fd]# cat 01-mmap_read.c
// 01-mmap_read.c#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>int main() {int fd;struct stat sb;char *mapped;fd = open("test.txt", O_RDONLY);if (fd == -1) {perror("open");exit(1);}// 2. 獲取文件狀態(主要是大小)if (fstat(fd, &sb) == -1) {perror("fstat");exit(1);}// 3. 映射文件到內存mapped = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);if (mapped == MAP_FAILED) {perror("mmap");close(fd);exit(1);}// 4. 像訪問數組一樣讀取內容printf("文件內容:\n");for (off_t i = 0; i < sb.st_size; i++) {putchar(mapped[i]); // 逐字符打印內存映射文件(mmap)的內容}// 5. 解除映射if (munmap(mapped, sb.st_size) == -1) {perror("munmap");}close(fd);return 0;
}
mmap追加文件內容
[root@prs31 01-fd]# cat 02-mmap_write.c
// mmap_write.c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <string.h>int main() {int fd;struct stat sb;char *mapped;const char *new_content = "\nAppended via mmap!";// 1. 以讀寫方式打開文件fd = open("test.txt", O_RDWR);if (fd == -1) {perror("open");exit(1);}// 2. 獲取文件大小if (fstat(fd, &sb) == -1) {perror("fstat");exit(1);}// 3. 擴展文件大小(為追加內容預留空間)off_t new_size = sb.st_size + strlen(new_content);if (ftruncate(fd, new_size) == -1) {perror("ftruncate");close(fd);exit(1);}// 4. 映射整個新大小的文件,這里傳遞使用擴展后的文件大小mapped = mmap(NULL, new_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);if (mapped == MAP_FAILED) {perror("mmap");close(fd);exit(1);}// 5. 在原內容末尾寫入新內容,strcpy(目的變量,要添加的變量)strcpy(mapped + sb.st_size, new_content);printf("已追加內容到文件\n");if (munmap(mapped, new_size) == -1) {perror("munmap");}close(fd);return 0;
}
mmap實現進程間共享內存
[root@prs31 01-fd]# cat 03-shm_writer.c
// 03-shm_writer.c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <string.h>int main() {int fd;char *mapped;// 1. 創建共享文件fd = open("/tmp/shm_file", O_CREAT | O_RDWR, 0644);if (fd == -1) {perror("open");exit(1);}// 2. 擴展文件大小ftruncate(fd, 4096); // 1頁// 3. 映射mapped = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);if (mapped == MAP_FAILED) {perror("mmap");exit(1);}// 4. 寫入數據sprintf(mapped, "Hello from Process A (PID: %d)", getpid());printf("寫入: %s\n", mapped);printf("等待按回車繼續...\n");getchar(); // 等待進程B讀取munmap(mapped, 4096);close(fd);return 0;
}
[root@prs31 01-fd]# cat 03-shm_reader.c
// 03-shm_writer
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/mman.h>int main() {int fd;char *mapped;fd = open("/tmp/shm_file", O_RDONLY);if (fd == -1) {perror("open");exit(1);}mapped = mmap(NULL, 4096, PROT_READ, MAP_SHARED, fd, 0);if (mapped == MAP_FAILED) {perror("mmap");exit(1);}printf("讀取到: %s\n", mapped);munmap(mapped, 4096);close(fd);return 0;
}
運行代碼
[root@prs31 01-fd]# gcc 03-shm_writer.c shm_writer
[root@prs31 01-fd]# gcc 03-shm_reader.c shm_reader
第二個終端: