进程间通信---共享存储映射

发布于:2023-01-16 ⋅ 阅读:(338) ⋅ 点赞:(0)

存储映射I/O能将一个磁盘文件映射到存储空间中的一个缓冲区上,

从缓冲区读数据就相当于读文件中的相应字节,将数据写入缓冲区,就相当于对文件写入相应字节

这样,就可在不适用read和write函数的情况下,使用地址(指针)完成I/O操作。

存储映射函数

创建:

#include<sys/mman.h>

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

功能:

 一个文件或者其它对象映射进内存

参数:

  •  addr : 指定映射的起始地址, 通常设为NULL, 由系统指定
  •  length:映射到内存的文件长度
  •  prot:  映射区的保护方式, 最常用的 :
    •  a) 读:PROT_READ
    •  b) 写:PROT_WRITE
    • c) 读写:PROT_READ | PROT_WRITE

 flags: 映射区的特性, 可以是

  •  a) MAP_SHARED : 写入映射区的数据会复制回文件, 且允许其他映射该文件的进程共享。
  •  b) MAP_PRIVATE : 对映射区的写入操作会产生一个映射区的复制(copy - on - write), 对此区域所做的修改不会写回原文件。

 fd:由open返回的文件描述符, 代表要映射的文件。

 offset:以文件开始处的偏移量, 必须是4k的整数倍, 通常为0, 表示从文件头开始映射

返回值:

成功:返回创建的映射区首地址

失败:MAP_FAILED宏

 解除映射

#include <sys/mman.h>

int munmap(void *addr, size_t length);

功能:

     释放内存映射区

参数:

     addr:使用mmap函数创建的映射区的首地址

     length:映射区的大小

返回值:

     成功:0

     失败:-1

#include<stdio.h>    
#include<sys/mman.h>    
#include<unistd.h>    
#include<sys/stat.h>    
#include<sys/types.h>    
#include<fcntl.h>    
#include<string.h>    
int main()    
{    
    int fd=open("txt",O_RDWR);    
    if(fd==-1)    
    {    
        perror("open");    
        return 1;    
    }    
    void *addr=NULL;    
    addr=mmap(addr,1000,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);    
    if(addr==MAP_FAILED)    
    {    
        perror("mmap");    
        return 1;    
    }    
    printf("创建映射成功............\n");    
   //向文件写数据    
   strcpy((char*)addr,"this is ret");    
   int ret=munmap(addr,1000);                                                                                                                                              
   if(ret==-1)    
   {    
       perror("munmap");    
       return 1;    
   }    
    return 0;
}

 

 注意事项:

  •   创建映射区的过程,隐含一次对映射文件的读操作
  • 当MAP_SHAREAD,要求:映射区的权限应<=文件打开的权限(出于对映射区的保护).
  • 映射区的释放对文件关闭无关,只要建立成功文件.可以立即关闭
  • 特别注意:当映射文件大小为0时,不能创建映射区,所以,用于映射的文件必须要有实际大小mmap使用时长会出现总线错误,通常是由于共享文件存储空间大小引起的
  • 文件偏移量必须为4k的整数倍(最后一个参数偏移量必须是4K的整数倍,这是操作系统的限制,文件最小门槛就是8个512,也就是4096。如果一个文件本身大小没有达到4K,那么它占用的空间大小也是4096。比如下面mem.txt实际上只有10字节大小,但是占据了8个512,这就证明文件最小占据4K空间。)

使用mmap实现父子进程通信

#include<stdio.h>    
#include<unistd.h>    
#include<sys/mman.h>    
#include<sys/types.h>    
#include<sys/stat.h>    
#include<fcntl.h>    
#include<stdlib.h>    
#include<string.h>    
#include<sys/wait.h>    
int main()    
{    
    int fd=open("txt",O_RDWR);    
    if(fd==-1)    
    {    
        perror("open");    
        return 1;    
    }    
    //存储映射    
    void *addr=NULL;    
    addr=mmap(addr,10,PROT_WRITE|PROT_READ,MAP_SHARED,fd,0);    
    if(addr==MAP_FAILED)    
    {    
        perror("mmap");    
        return 1;    
    }    
    //创建父子进程                                                                                                                                                         
    pid_t pid=-1;    
    pid=fork();    
    if(pid==0)    
    {    
       sleep(2);
       printf("%s\n",(char*)addr);                                                                                                                                         

    }
    else{
        //parent
        strcpy((char*)addr,"123456789");
        wait(NULL);
    }
   int ret= munmap(addr,10);
   if(ret==-1)
   {
       perror("munmap");
       return 1;
   }
    return 0;
}
    

匿名映射实现父子进程通信

无需依赖一个文件即可创建映射区。同样需要借助标志位参数flags来指定。

使用MAP_ANONYMOUS (或MAP_ANON)

int *p = mmap(NULL, 4, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);

  • 4"随意举例,该位置表示映射区大小,可依实际需要填写。
  • MAP_ANONYMOUS和MAP_ANON这两个宏是Linux操作系统特有的宏。在类Unix系统中如无该宏定义,可使用如下两步来完成匿名映射区的建立。
#include<stdio.h>    
#include<string.h>    
#include<sys/types.h>    
#include<sys/wait.h>    
#include<unistd.h>    
#include<stdlib.h>    
#include<fcntl.h>    
#include<sys/mman.h>    
int main()    
{    
    void *addr=NULL;    
   addr= mmap(addr,10,PROT_READ|PROT_WRITE,MAP_ANON|MAP_SHARED,-1,0);    
   if(addr==MAP_FAILED)    
   {    
       perror("mmap");    
       return 1;    
   }    
   pid_t pid =-1;    
   pid=fork();    
   if(pid==0)    
   {    
       //child    
       sleep(2);    
       printf("%s\n",(char*)addr);    
   }    
   else                                                                                                                                                                    
   {    
       strcpy((char*)addr,"123456789w");    
       wait(NULL);    
   }    
   int ret=munmap(addr,10);  
   if(ret<0)
   {
       perror("munmap");
       return 1;
   }
    return 0;
}


网站公告

今日签到

点亮在社区的每一天
去签到