Linux下IPC方式之共享存储映射(mmap)

1. 共享存储映射(mmap)

把文件中的某一段映射到内存上
在这里插入图片描述

mmap函数原型:

#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

参数:

addr 			建立映射区的首地址,由Linux内核指定。使用时,直接传递NULL

length 			映射区长度

prot 
PROT_READ 		可读
PROT_WRITE		可写

flags
MAP_SHARED 		共享的,对内存的修改会影响到源文件
MAP_PRIVATE		私有的

fd 				文件描述符

offset			偏移量

返回值
成功 			返回 可用内存的首地址
失败 			返回 MAP_FAILED

释放内存区

#include <sys/mman.h>
int munmap(void *addr, size_t length);
addr 	传mmap返回值

length 	mmap创建的长度

返回值
成功:0; 失败:-1

在这里插入图片描述
实例(MAP_SHARED的作用是,你修改了内存,会影响文件)


#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("men.txt", O_RDWR);
	//创建映射区
	char *mem=(char*)mmap(NULL, 8, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
 
	if(mem==MAP_FAILED){
		perror("mmap err");
		return -1;
	}
 
	//拷贝数据
	strcpy(mem, "hello");
 
	//释放mmap
	munmap(mem, 8);
	close(fd);
	return 0;
}

2. mmap九问

  1. 如果更改mem变量的地址,释放的时候munmap,传入mem还能成功吗?
  2. 如果对mem越界操作会怎样?
  3. 如果文件偏移量随便填个数会怎样?
  4. 如果文件描述符先关闭,对mmap映射有没有什么影响?
  5. open的时候,可以新创建一个文件来创建映射区吗?
  6. open文件选择O_WRONLY,可以吗?
  7. 当选择MAP_SHARED的时候,open文件选择O_RDONLY,prot可以选择PROT_READ|PROT_WRITE吗?
  8. mmap什么情况下会报错?
  9. 如果不判断返回值会怎么样?

1.如果更改mem变量的地址,释放的时候munmap,传入mem还能成功吗?
问题一的测试:(不能改,否则会释放失败)

#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("men.txt", O_RDWR);
	//创建映射区
	char *mem=mmap(NULL, 8, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
 
	if(mem==MAP_FAILED){
		perror("mmap err");
		return -1;
	}
 
	//拷贝数据
	strcpy(mem, "hello");
	mem++;
	//如果释放失败
	if (munmmap(mem, 8) < 0) {
		perror("munmmap err");
	}
	close(fd);
	return 0;
}

运行结果:
在这里插入图片描述
2.如果对mem越界操作会怎样?
问题二的测试:

#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("men.txt", O_RDWR);
	//创建映射区
	char *mem=mmap(NULL, 8, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
 
	if(mem==MAP_FAILED){
		perror("mmap err");
		return -1;
	}
 
	//拷贝数据
	strcpy(mem, "hellollllllllllll");
 
	//释放内存
	munmmap(mem, 8);
	close(fd);
	return 0;
}

文件的大小对映射区操作有影响,尽量避免。
3.如果文件偏移量随便填个数会怎样?

char *mem=mmap(NULL, 8, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 1000000000);

在这里插入图片描述
offset必须是4k的整数倍。

4.如果文件描述符先关闭,对mmap映射有没有什么影响?
没有。因为mmap之后,通道就打通了,文件就没用了。
5.open的时候,可以新创建一个文件来创建映射区吗?

int fd=open("men.txt", O_RDWR|O_TRUNC, 0664);//创建并截断文件

如果新创建的文件是空的,会报错。
如果文件大小不为0,则可以。

6.open文件选择O_WRONLY,可以吗?
不可以,映射到内存的时候隐含一次读操作,如果只有写权限,则会报错
7.当选择MAP_SHARED的时候,open文件选择O_RDONLY,prot可以选择PROT_READ|PROT_WRITE吗?
不可以。SHARED的时候,映射区的权限要小于等于open文件的权限。

3. 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>
 
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;
}

运行结果
在这里插入图片描述
将上面的注释去掉,改成


//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);

运行结果
在这里插入图片描述
父进程并没有读到子进程的数据,子进程也没有读到父进程改的数据。
如果要实现父子进程之间通信,需要将flags设为MAP_PRIVATE
在这里插入图片描述

4. 匿名映射

避免打开文件的操作(上面的例子都有调用open函数)

int *mem = mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANON,-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;
}

运行结果
在这里插入图片描述
注意:有的Unix系统中没有MAP_ANON,ANONYMOUS这两个宏。此时该怎么办?
此时用这个
在这里插入图片描述
/dev/zero 是一个聚宝盆,无限大,用它做匿名映射,你想取多大都可以。
另外一个,/dev/null 是一个无底洞,一般错误信息重定向到这个文件中
小技巧——快速把一个文件头几行的数据重定向到另一个文件中(会覆盖目标文件)
在这里插入图片描述

5. 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[])
{
    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);
	//将文件大小改变为参数length指定的大小,
	//如果原来的文件大小比参数length大,则超过的部分会被删除,
	//如果原来的文件大小比参数length小,则文件将被扩展
    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;
}

读端

#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;
}

运行结果:
在这里插入图片描述
再开一个读的:
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值