【轉載】原文鏈接:https://blog.csdn.net/hj605635529/article/details/73163513
linux中的兩種共享內存。一種是我們的IPC通信System V版本的共享內存,另外的一種就是我們今天提到的存儲映射I/O(mmap函數)
在說mmap之前我們先說一下普通的讀寫文件的原理,進程調用read或是write后會陷入內核,因為這兩個函數都是系統調用,進入系統調用后,內核開始讀寫文件,假設內核在讀取文件,內核首先把文件讀入自己的內核空間,讀完之后進程在內核回歸用戶態,內核把讀入內核內存的數據再copy進入進程的用戶態內存空間。實際上我們同一份文件內容相當於讀了兩次,先讀入內核空間,再從內核空間讀入用戶空間。
Linux提供了內存映射函數mmap, 它把文件內容映射到一段內存上(准確說是虛擬內存上), 通過對這段內存的讀取和修改, 實現對文件的讀取和修改,mmap()系統調用使得進程之間可以通過映射一個普通的文件實現共享內存。普通文件映射到進程地址空間后,進程可以向訪問內存的方式對文件進行訪問,不需要其他系統調用(read,write)去操作。
mmap圖示例:
mmap系統調用介紹
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
這就是mmap系統調用的接口,mmap函數成功返回指向內存區域的指針,圖上的進程的地址空間的開始地址就是mmap函數的返回值,失敗返回MAP_FAILED。
addr,某個特定的地址作為起始地址,當被設置為NULL,系統會在地址空間選擇一塊合適的內存區域。
length說的是內存段的長度。
prot是用來設定內存段的訪問權限。
prot參數 | 說明 |
---|---|
PROT_READ | 內存段可讀 |
PROT_WRITE | 內存段可寫 |
PROT_EXEC | 內存段可執行 |
PROT_NONE | 內存段不能被訪問 |
flags參數控制內存段內容被修改以后程序的行為。
flags參數 | 說明 |
---|---|
MAP_SHARED | 進程間共享內存,對該內存段修改反映到映射文件中。提供了POSIX共享內存 |
MAP_PRIVATE | 內存段為調用進程所私有。對該內存段的修改不會反映到映射文件 |
MAP_ANNOYMOUS | 這段內存不是從文件映射而來的。內容被初始化為全0 |
MAP_FIXED | 內存段必須位於start參數指定的地址處,start必須是頁大小的整數倍(4K整數倍) |
MAP_HUGETLB | 按照大內存頁面來分配內存空間 |
fd參數是用來被映射文件對應的文件描述符。通過open系統調用得到。offset設定從何處進行映射。
mmap使用注意事項:
#include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<fcntl.h> #include<sys/types.h> #include<sys/stat.h> #include<sys/mman.h> #include<string.h> struct STU { int age; char name[20]; char sex; }; int main(int argc,char *argv[]) //這個進程用於創建映射區進行寫。 { if(argc != 2) { printf("./a,out file"); exit(1); } struct STU student = {10,"xiaoming",'m'}; int fd = open(argv[1],O_RDWR|O_CREAT|O_TRUNC,0644); if(fd < 0) { perror("open"); exit(2); } ftruncate(fd,sizeof(struct STU)); //文件拓展大小。 struct STU *p = (struct STU*)mmap(NULL,sizeof(struct STU),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);//創建一個結構體大小的共享映射區。共享映射區我們可以當做數組區看待。 if(p == MAP_FAILED) { perror("mmap"); exit(3); } close(fd); //關閉不用的文件描述符。 while(1) { memcpy(p,&student,sizeof(student)); student.age++; sleep(1); } int ret = munmap(p,sizeof(student)); if(ret < 0) { perror("mmumap"); exit(4); } return 0; }
#include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<fcntl.h> #include<sys/types.h> #include<sys/stat.h> #include<sys/mman.h> struct STU { int age; char name[20]; char sex; }; int main(int argc,char *argv[]) //這個進程讀 { if(argc != 2) { printf("./a,out file"); exit(1); } int fd = open(argv[1],O_RDONLY,0644); if(fd < 0) { perror("open"); exit(2); } struct STU student; struct STU *p = (struct STU*)mmap(NULL,sizeof(struct STU),PROT_READ,MAP_SHARED,fd,0); if(p == MAP_FAILED) { perror("mmap"); exit(3); } close(fd); int i = 0; while(1) { printf("id = %d\tname = %s\t%c\n",p->age,p->name,p->sex); sleep(2); } int ret = munmap(p,sizeof(student)); if(ret < 0) { perror("mmumap"); exit(4); } return 0; }
代碼截圖:
分析:因為只創建一個結構體大小的共享內存,后面寫入的數據把前面寫入的數據覆蓋了。
shm調用介紹:參見上一篇博客

(2)通過void *shmat(int shmid, constvoid shmaddr,int shmflg);連接成功后把共享內存區對象映射到調用進程的地址空間
(3)通過void *shmdt(constvoid* shmaddr);斷開用戶級頁表到共享內存的那根箭頭。
(4)通過int shmctl(int shmid, int cmd, struct shmid_ds* buf);釋放物理內存中的那塊共享內存。
1、mmap是在磁盤上建立一個文件,每個進程地址空間中開辟出一塊空間進行映射。
而對於shm而言,shm每個進程最終會映射到同一塊物理內存。shm保存在物理內存,這樣讀寫的速度要比磁盤要快,但是存儲量不是特別大。
2、相對於shm來說,mmap更加簡單,調用更加方便,所以這也是大家都喜歡用的原因。
3、另外mmap有一個好處是當機器重啟,因為mmap把文件保存在磁盤上,這個文件還保存了操作系統同步的映像,所以mmap不會丟失,但是shmget就會丟失。