Linux多进程开发——进程间通信 内存映射

内存映射是一种将磁盘文件直接映射到内存的技术,允许通过修改内存来修改文件。mmap函数用于创建内存映射,参数包括文件描述符、权限等。进程间通信可以通过共享内存映射区实现,父子进程可以读写同一区域。此外,内存映射也可用于文件复制,通过映射源文件和目标文件到内存并进行数据拷贝。匿名映射则无需文件,直接在进程间创建共享内存。
摘要由CSDN通过智能技术生成

内存映射(Memory-mmaped I/O)

内存映射是将磁盘文件的数据映射到内存,用户通过修改内存就能修改磁盘文件。

507c55358236440d8624cae1ff7a4363.jpeg

 

 

  • 内存映射相关系统调用

    • mmap函数

      #include<sys/mman.h>
      void *mmap(void *addr,size_t length,int prot,int flags,int fd,off_t offset);
      • 功能:将一个文件或设备的数据映射到内存中

      • 参数:

        -addr:地址,一般设置为NULL,有内核指定

        -length:要映射的数据的长度,不能为0。建议使用文件的长度。

        获取文件长度:lseek stat

        -prot:对申请的内存映射区的操作权限,可以设置为:

        -PROT_EXEC:可执行权限

        -PROT_READ:读权限

        -PROT_WRITE:写权限

        -PROT_NONE:无权限

        要操作映射内存,必须要有读权限。

        -flags:

        -MAP_SHARED:映射区的数据会自动和磁盘文件同部,进程间通信必须设置这个选项

        -MAP_PRIVATE:不同步,内存映射区的数据改变了,对原来的文件不会修改,会重新创建一个新的文件(copy on write)

        -MAP_ANONYMOUS:匿名映射

        -fd:需要映射的那个文件的文件描述符

        通过open得到,open的是一个磁盘文件。

        注意:文件的大小不能为0,open指定的权限不能和prot参数有冲突。

        -off_t:偏移量,一般不用。必须指定为4k的整数倍,0表示不偏移。

      • 返回值:

        成功:返回创建的内存的首地址

        失败:返回MAP_FAILED(其实就是void* -1),并设置errno

    • munmap函数

      #include<sys/mman.h>
      int munmap(void *addr,size_t length);
      • 功能:释放内存映射

      • 参数:

        -addr:要释放的内存的首地址

        -length:要释放的内存的大小,要和mmap函数中的length参数的值一样

使用内存映射实现进程间通信:

  1. 有关系的进程(父子进程)

    (1)还没有子进程时,通过唯一的父进程先创建内存映射区;

    (2)有了内存映射区后创建子进程

    (3)父子进程共享创建的内存映射区

    #include<stdio.h>
    #include<sys/mman.h>
    #include<fcntl.h>
    #include<sys/types.h>
    #include<sys/wait.h>
    #include<unistd.h>
    #include<string.h>
    #include<stdlib.h>
    ​
    int main(){
    ​
        //1.打开一个文件
        int fd=open("test.txt",O_RDWR);
        //获取文件的大小
        int size=lseek(fd,0,SEEK_END);
    ​
        //2.创建内存映射区
        void *ptr=mmap(NULL,size,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
        if(ptr==MAP_FAILED){
            perror("mmap");
            exit(0);
        }
    ​
        //3.创建子进程
        pid_t pid=fork();
        if(pid>0){
            //父进程
            wait(NULL);
            char buf[64];
            strcpy(buf,(char*)ptr);
            printf("read data :%s\n",buf);
            
        }else if(pid==0){
            //子进程
            strcpy((char*)ptr,"hello,son!");
        }
        //关闭内存映射区
        munmap(ptr,size);
        return 0;
    }

     

  2. 没有关系的进程间通信

    (1)准备一个大小不是0的磁盘文件

    (2)进程1:通过磁盘文件创建内存映射区,并得到一个操作这块内存的指针

    进程2:通过磁盘文件创建内存映射区,并得到一个操作这块内存的指针

    (3)使用内存映射区通信

    注意:内存映射区通信不会阻塞

  • 内存映射的注意事项

    • 对mmap的返回值(ptr)直接做++操作,而没有保存,munmap会出错

      void *ptr=mmap(...);
      ptr++;//可以对其进行++操作
      munmap(ptr,len);//错误,要保存地址
    • prot参数的权限应少于或等于open()函数的权限,否则会出错。

      建议open()函数中的权限和prot参数的权限保持一致。

    例如:open时的权限为O_RDONLY,mmap时prot参数指定PROT_READ | PROT_WRITE会出错,mmap会返回MAP_FAILED

    • 偏移量必须是4k的整数倍,否则会返回MAP_FAILED

    • mmap调用失败的情况:

      • 第一个参数:length=0

      • 第三个参数:prot

        -只指定了写权限

        -prot参数指定的权限与open函数指定的权限冲突

    • 可以通过open的时候O_CREAT一个新文件来创建映射区,但是创建的文件大小不能为0,否则会调用失败,可以用lseek或者truncate函数对新的文件进行扩展

    • mmap后关闭文件描述符,对mmap映射没有影响

      int fd=open("xxx");
      mmap(...,fd,0);
      close(fd);
      ​
      //mmap映射区还存在,只是创建映射区的fd被关闭,没有任何影响。
  • 复制文件

    内存映射处理用于进程间通信,还可以进行文件复制

    思路:

    (1)对原文件的内容进行内存映射

    (2)创建一个新文件(拓展该文件)

    (3)把新文件的数据映射到内存中

    (4)通过内存拷贝将第一个文件的内存数据拷贝到新的文件内存中

    (5)释放资源

    代码实例:

    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/mman.h>
    #include<sys/types.h>
    #include<sys/stat.h>
    #include<unistd.h>
    #include<fcntl.h>
    #include<string.h>
    ​
    int main(){
        //1.对原始文件进行内存映射
        int fd=open("english.txt",O_RDWR);
        if(fd==-1){
            perror("open");
            exit(0);
        }
    ​
        //获取原始文件的大小
        int size=lseek(fd,0,SEEK_END);
    ​
        //2.创建新文件(拓展该文件)
        int fd1=open("cpy.txt",O_RDWR | O_CREAT,0664);
        if(fd==-1){
            perror("open");
            exit(0);
        }
    ​
        //对新文件进行拓展
        truncate("cpy.txt",size);
        write(fd1," ",1);
    ​
        //3.分别做内存映射
        void *ptr=mmap(NULL,size,PROT_READ |PROT_WRITE,MAP_SHARED,fd,0);
        void *ptr1=mmap(NULL,size,PROT_READ |PROT_WRITE,MAP_SHARED,fd1,0);
    ​
        if(ptr==MAP_FAILED){
            perror("mmap");
            exit(0);
        }
    ​
        if(ptr1==MAP_FAILED){
            perror("mmap");
            exit(0);
        }
    ​
        //4.内存拷贝
        memcpy(ptr1,ptr,size);
    ​
        //5.释放资源
        munmap(ptr1,size);
        munmap(ptr,size);
    ​
        close(fd1);
        close(fd);
    }

     

  • 匿名映射:不需要文件实体进程的内存映射

    通过匿名映射实现进程间的通信:

    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/mman.h>
    #include<sys/types.h>
    #include<sys/stat.h>
    #include<unistd.h>
    #include<fcntl.h>
    #include<string.h>
    #include<wait.h>
    ​
    int main(){
    ​
        //1.创建匿名内存映射区
        int len=4096;
        void *ptr=mmap(NULL,len,PROT_READ | PROT_WRITE,MAP_SHARED | MAP_ANONYMOUS,-1,0);
        if(ptr==MAP_FAILED){
            perror("mmap");
            exit(0);
        }
    ​
        //2.父子进程间通信
        pid_t pid=fork();
        if(pid>0){
            //父进程
            strcpy((char*)ptr,"hello,world");
            wait(NULL);
        }else if(pid==0){
            //子进程
            sleep(1);
            printf("I am child,pid:%d,info:%s\n",getgid(),(char*)ptr);
        }
    ​
        //3.释放内存映射区
        int ret=munmap(ptr,len);
        if(ret==-1){
            perror("munmap");
            exit(0);
        }
    ​
        return 0;
    }

     

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值