Liunx操作-Record20—MMAP共享映射区相关的操作

目录

MMP共享映射的原理

创建映射区函数

释放映射区函数

示例说明

mmap九问

mmap实现父子进程通信

匿名映射

用mmap支持无血缘关系进程通信

关于"mmap_MAP_SHARED"的强调


MMP共享映射的原理

把文件中的某一段,做个映射到mmap中(在内存中开辟的一块儿内存区),将文件中的某一块儿截取出来给映射到内存中去,这样的话,直接操作内存就可以使用了,这样比文件操作就快了很多。

创建映射区函数

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

  • addr 传NULL
  • length 映射区的长度
  • prot 
    • PROT_READ 可读
    • PROT_WRITE 可写
  • flags
    • MAP_SHARED 共享的,对内存的修改会影响到源文件
    • MAP_PRIVATE 私有的
  • fd 文件描述符,open打开一个文件
  • offset  偏移量
  • 返回值
    • 成功 返回 可用的内存首地址
    • 失败 返回 MAP_FAILED

释放映射区函数

                    int munmap(void *addr, size_t length);

  • addr 传mmap的返回值
  • length mmap创建的长度
  • 返回值

示例说明

先在路径下面创建一个mem.txt,来作为一个接受数据的文件。

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <string.h>

int main()
{
    int fd = open("mem.txt",O_RDWR);//创建并且截断文件
    //int fd = open("mem.txt",O_RDWR|O_CREAT|O_TRUNC,0664);//创建并且截断文件

    ftruncate(fd,8);   
    //创建映射区
   char *mem = mmap(NULL,20,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);  //这就是最头疼的填参数部分,相当于得到一块儿内存
    //char *mem = mmap(NULL,8,PROT_READ|PROT_WRITE,MAP_PRIVATE,fd,0);

    if(mem == MAP_FAILED){
        perror("mmap err");
        return -1;
    }
    close(fd);
    //拷贝数据
    strcpy(mem,"helloworld");
//    mem++;
    //释放mmap
    if(munmap(mem,20) < 0){
        perror("munmap err");
    }
    return 0;
}

总体流程是,从进程中将数据映射到映射区,再从映射区拷贝到mem.txt中。

mmap九问

  1. 如果更改mem变量的地址,释放的时候munmap,传入mem还能成功吗?不能!!要不然释放的时候释放不了
  2. 如果对mem越界操作会怎么样?文件的大小对映射区操作有影响,尽量避免。
  3. 如果文件偏移量随便填个数会怎么样? offset必须是 4k的整数倍
  4. 如果文件描述符先关闭,对mmap映射有没有影响?没有影响
  5. open的时候,可以新创建一个文件来创建映射区吗?不可以用大小为0的文件,即不能用新创建的,没扩展的文件。
  6. open文件选择O_WRONLY,(如:int fd = open("mem.txt",O_WRONLY))可以吗? 不可以: Permission denied,这是当把文件映射到缓冲器的时候i,这个时候,包含了一次读的操作,必须有读权限才行。
  7. 当选择MAP_SHARED的时候,open文件选择O_RDONLY,prot可以选择PROT_READ|PROT_WRITE吗?Permission denied ,SHARED的时候,必须要保证:映射区的权限 <= open文件的权限
  8. mmap什么情况下会报错?很多情况
  9. 如果不判断返回值会怎么样? 会死的很难堪!!

mmap实现父子进程通信

整个过程,就是将父子进程的内容都可以拿到映射区去处理,其中一个进行修改,另一个去读的时候有变化,顺序方面,应该先创建映射区,然后再进行fork创建子进程。

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/wait.h>

int main()
{
    // 先创建映射区
    int fd = open("mem.txt",O_RDWR);
    //int *mem = mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    int *mem = mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_PRIVATE,fd,0);
    if(mem == MAP_FAILED){
        perror("mmap err");
        return -1;
    }
    // fork子进程
    pid_t pid = fork();

    // 父进程和子进程交替修改数据
    if(pid == 0 ){
        //son 
        *mem = 100;
        printf("child,*mem = %d\n",*mem);
        sleep(3);
        printf("child,*mem = %d\n",*mem);
    }
    else if(pid > 0){
        //parent
        sleep(1);
        printf("parent,*mem=%d\n",*mem);
        *mem = 1001;
        printf("parent,*mem=%d\n",*mem);
        wait(NULL);
    }

    munmap(mem,4);  //最后记得回收缓冲区
    close(fd);
    return 0;
}

父子进程都可以给缓冲区存数据。运行结果如下:

在写这段代码中的时候,在父子进程通信的过程中,父进程修改一个数值,然后,再传给子进程。可以发现"mem,txt"这个文件,作为一个中间载体,需要新创建一个文件,是非常鸡肋的,就是打开存了个数据,然后,又取出来了,为了弥补这个缺陷,就有了匿名映射的概念。

匿名映射

通过使用我们发现,使用映射区来完成文件读写操作十分方便,父子进程间通信也较容易。但缺陷是,每次创建映射区一定要依赖一个文件才能实现。通常为了建立映射区要open一个temp文件,创建好了再unlink、close掉,比较麻烦。 可以直接使用匿名映射来代替。其实Linux系统给我们提供了创建匿名映射区的方法,无需依赖一个文件即可创建映射区。同样需要借助标志位参数flags来指定。可以通过使用MAP_ANONYMOUS (或MAP_ANON)来完成。

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

下面是例子: 

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/wait.h>

int main()
{
    int *mem = mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANON,-1,0);

    if(mem == MAP_FAILED){
        perror("mmap err");
        return -1;
    }

    pid_t pid = fork();

    if(pid == 0 ){
        //son 
        *mem = 101;
        printf("child,*mem=%d\n",*mem);
        sleep(3);
        printf("child,*mem=%d\n",*mem);
    }else if(pid > 0){
        //parent 
        sleep(1);
        printf("parent,*mem=%d\n",*mem);
        *mem = 10001;
        printf("parent,*mem=%d\n",*mem);
        wait(NULL);
    }

    munmap(mem,4);
    return 0;
}

其实,这个逻辑跟上个代码的逻辑一模一样,只不过是通过匿名的方式,没有再新创建一个文件而已。

了解:因为MAP_ANON,ANONYMOUS 这两个宏在有些unix系统没有,但可以使用/dev/zero文件中打开

  • /dev/zero  聚宝盆 ,可以随意映射

  • /dev/null   无底洞  ,一般错误信息重定向到这个文件中 

用mmap支持无血缘关系进程通信

读过程,往结构体里面读入数据:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/wait.h>

typedef struct _Student{
    int sid;
    char sname[20];
}Student;

int main(int argc,char *argv[])
{
    //open file 
    int fd = open(argv[1],O_RDWR);
    //mmap 
    int length = sizeof(Student);
    Student *stu = mmap(NULL,length,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    if(stu == MAP_FAILED){
        perror("mmap err");
        return -1;
    }
    //read data 
    while(1){
        printf("sid=%d,sname=%s\n",stu->sid,stu->sname);
        sleep(1);
    }
    //close and munmap 
    munmap(stu,length);
    close(fd);
    return 0;
}

写操作:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/wait.h>

typedef struct  _Student{
    int sid;
    char sname[20];
}Student;

int main(int argc,char *argv[])
{
    if(argc != 2){
        printf("./a.out filename\n");
        return -1;
    }
    
    // 1. open file 
    int fd = open(argv[1],O_RDWR|O_CREAT|O_TRUNC,0666);
    int length = sizeof(Student);

    ftruncate(fd,length);

    // 2. mmap
    Student * stu = mmap(NULL,length,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    
    if(stu == MAP_FAILED){
        perror("mmap err");
        return -1;
    }
    int num = 1;
    // 3. 修改内存数据
    while(1){
        stu->sid = num;
        sprintf(stu->sname,"xiaoming-%03d",num++);
        sleep(1);//相当于没隔1s修改一次映射区的内容
    }
    // 4. 释放映射区和关闭文件描述符
    munmap(stu,length);
    close(fd);

    return 0;
}

关于"mmap_MAP_SHARED"的强调

如果进程之间有通信的话,必须是有共享的,即用“mmap_MAP_SHARED”,如果进程要通信,flags 必须设为 MAP_SHARED

    int *mem = mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0); //通信的时候
    //int *mem = mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_PRIVATE,fd,0); //不通信的时候

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值