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);
- 1
- 2
- 1
- 2
這就是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呼叫介紹:參見上一篇部落格
http://blog.csdn.net/hj605635529/article/details/67636526shm圖示例:
(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就會丟失。