mmap映射區和shm共享內存的區別總結


【轉載】原文鏈接: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使用注意事項:

利用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就會丟失。




 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM