mmap映射区和shm共享内存的区别总结
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使用注意事項:
利用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調用介紹:參見上一篇博客
http://blog.csdn.net/hj605635529/article/details/67636526
shm圖示例:
(1)通過int shmget(key_t key, size_t size, int shmflg);在物理內存創建一個共享內存,返回共享內存的編號。
(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);釋放物理內存中的那塊共享內存。
總結mmap和shm:
1、mmap是在磁盤上建立一個文件,每個進程地址空間中開辟出一塊空間進行映射。
而對于shm而言,shm每個進程最終會映射到同一塊物理內存。shm保存在物理內存,這樣讀寫的速度要比磁盤要快,但是存儲量不是特別大。
2、相對于shm來說,mmap更加簡單,調用更加方便,所以這也是大家都喜歡用的原因。
3、另外mmap有一個好處是當機器重啟,因為mmap把文件保存在磁盤上,這個文件還保存了操作系統同步的映像,所以mmap不會丟失,但是shmget就會丟失。
總結
以上是生活随笔為你收集整理的mmap映射区和shm共享内存的区别总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【mybatis-plus】什么是乐观锁
- 下一篇: 文件写入的6种方法,这种方法性能最好