mmap映射区和shm共享内存的区别总结
linux中的兩種共享內(nèi)存。一種是我們的IPC通信System V版本的共享內(nèi)存,另外的一種就是我們今天提到的存儲映射I/O(mmap函數(shù))
在說mmap之前我們先說一下普通的讀寫文件的原理,進(jìn)程調(diào)用read或是write后會(huì)陷入內(nèi)核,因?yàn)檫@兩個(gè)函數(shù)都是系統(tǒng)調(diào)用,進(jìn)入系統(tǒng)調(diào)用后,內(nèi)核開始讀寫文件,假設(shè)內(nèi)核在讀取文件,內(nèi)核首先把文件讀入自己的內(nèi)核空間,讀完之后進(jìn)程在內(nèi)核回歸用戶態(tài),內(nèi)核把讀入內(nèi)核內(nèi)存的數(shù)據(jù)再copy進(jìn)入進(jìn)程的用戶態(tài)內(nèi)存空間。實(shí)際上我們同一份文件內(nèi)容相當(dāng)于讀了兩次,先讀入內(nèi)核空間,再從內(nèi)核空間讀入用戶空間。
Linux提供了內(nèi)存映射函數(shù)mmap, 它把文件內(nèi)容映射到一段內(nèi)存上(準(zhǔn)確說是虛擬內(nèi)存上), 通過對這段內(nèi)存的讀取和修改, 實(shí)現(xiàn)對文件的讀取和修改,mmap()系統(tǒng)調(diào)用使得進(jìn)程之間可以通過映射一個(gè)普通的文件實(shí)現(xiàn)共享內(nèi)存。普通文件映射到進(jìn)程地址空間后,進(jìn)程可以向訪問內(nèi)存的方式對文件進(jìn)行訪問,不需要其他系統(tǒng)調(diào)用(read,write)去操作。
mmap圖示例:
mmap系統(tǒng)調(diào)用介紹
?
? void *mmap(void *addr, size_t length, int prot, int flags,
? ? ? ? ? ? ? ? ? int fd, off_t offset);
這就是mmap系統(tǒng)調(diào)用的接口,mmap函數(shù)成功返回指向內(nèi)存區(qū)域的指針,圖上的進(jìn)程的地址空間的開始地址就是mmap函數(shù)的返回值,失敗返回MAP_FAILED。
addr,某個(gè)特定的地址作為起始地址,當(dāng)被設(shè)置為NULL,系統(tǒng)會(huì)在地址空間選擇一塊合適的內(nèi)存區(qū)域。
length說的是內(nèi)存段的長度。
prot是用來設(shè)定內(nèi)存段的訪問權(quán)限。
prot參數(shù)?? ?說明
PROT_READ?? ?內(nèi)存段可讀
PROT_WRITE?? ?內(nèi)存段可寫
PROT_EXEC?? ?內(nèi)存段可執(zhí)行
PROT_NONE?? ?內(nèi)存段不能被訪問
flags參數(shù)控制內(nèi)存段內(nèi)容被修改以后程序的行為。
flags參數(shù)?? ?說明
MAP_SHARED?? ?進(jìn)程間共享內(nèi)存,對該內(nèi)存段修改反映到映射文件中。提供了POSIX共享內(nèi)存
MAP_PRIVATE?? ?內(nèi)存段為調(diào)用進(jìn)程所私有。對該內(nèi)存段的修改不會(huì)反映到映射文件
MAP_ANNOYMOUS?? ?這段內(nèi)存不是從文件映射而來的。內(nèi)容被初始化為全0
MAP_FIXED?? ?內(nèi)存段必須位于start參數(shù)指定的地址處,start必須是頁大小的整數(shù)倍(4K整數(shù)倍)
MAP_HUGETLB?? ?按照大內(nèi)存頁面來分配內(nèi)存空間
fd參數(shù)是用來被映射文件對應(yīng)的文件描述符。通過open系統(tǒng)調(diào)用得到。offset設(shè)定從何處進(jìn)行映射。
mmap使用注意事項(xiàng):
利用mmap進(jìn)行非血緣進(jìn)程間通信代碼:
?#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[]) //這個(gè)進(jìn)程用于創(chuàng)建映射區(qū)進(jìn)行寫。
{
?? ?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);//創(chuàng)建一個(gè)結(jié)構(gòu)體大小的共享映射區(qū)。共享映射區(qū)我們可以當(dāng)做數(shù)組區(qū)看待。
?? ?if(p == MAP_FAILED)
?? ?{
?? ??? ?perror("mmap");
?? ??? ?exit(3);
?? ?}
?? ?close(fd); //關(guān)閉不用的文件描述符。
?? ?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[]) //這個(gè)進(jìn)程讀
{
?? ?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;
}
代碼截圖:
分析:因?yàn)橹粍?chuàng)建一個(gè)結(jié)構(gòu)體大小的共享內(nèi)存,后面寫入的數(shù)據(jù)把前面寫入的數(shù)據(jù)覆蓋了。
shm調(diào)用介紹:參見上一篇博客
http://blog.csdn.net/hj605635529/article/details/67636526
shm圖示例:
(1)通過int shmget(key_t key, size_t size, int shmflg);在物理內(nèi)存創(chuàng)建一個(gè)共享內(nèi)存,返回共享內(nèi)存的編號。
(2)通過void *shmat(int shmid, constvoid shmaddr,int shmflg);連接成功后把共享內(nèi)存區(qū)對象映射到調(diào)用進(jìn)程的地址空間
(3)通過void *shmdt(constvoid* shmaddr);斷開用戶級頁表到共享內(nèi)存的那根箭頭。
(4)通過int shmctl(int shmid, int cmd, struct shmid_ds* buf);釋放物理內(nèi)存中的那塊共享內(nèi)存。
總結(jié)mmap和shm:
1、mmap是在磁盤上建立一個(gè)文件,每個(gè)進(jìn)程地址空間中開辟出一塊空間進(jìn)行映射。
而對于shm而言,shm每個(gè)進(jìn)程最終會(huì)映射到同一塊物理內(nèi)存。shm保存在物理內(nèi)存,這樣讀寫的速度要比磁盤要快,但是存儲量不是特別大。
2、相對于shm來說,mmap更加簡單,調(diào)用更加方便,所以這也是大家都喜歡用的原因。
3、另外mmap有一個(gè)好處是當(dāng)機(jī)器重啟,因?yàn)閙map把文件保存在磁盤上,這個(gè)文件還保存了操作系統(tǒng)同步的映像,所以mmap不會(huì)丟失,但是shmget就會(huì)丟失。
總結(jié)
以上是生活随笔為你收集整理的mmap映射区和shm共享内存的区别总结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【mybatis-plus】什么是乐观锁
- 下一篇: 文件写入的6种方法,这种方法性能最好