Linux基础——内存共享映射(mmap函数的使用)

进程间的通信就像两人的交流,需要一个“公共场所”常见的有1、内核 2、内存 3、磁盘。

mmap

  • 用途:进程间的通信
  • 使用原理:把磁盘的一个空间映射到内存中,如下图所示
Linux基础——内存共享映射(mmap函数的使用)_第1张图片
  • 函数:void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
  • 参数解析
    • 参数1:希望在内存的哪个位置(即内存地址),传NULL,由系统自动找一块内存空间。一般都写NULL

    • 参数2:请内存长度(可以使用lseek函数来测试文件大小)

    • 参数3:prot页面属性(映射到内存的这一段的权限),就是对这个文件是要读还是写,可以填写的参数为:

      • PROT_EXEC表示映射的这一段可执行,例如映射共享库
      • PROT_READ表示映射的这一段可读
      • PROT_WRITE表示映射的这一段可写
      • PROT_NONE表示映射的这一段不可访问
    • 参数4:状态标志,有两种:共享映射MAP_SHARED 和 私有映射MAP_PRIVATE

      • 共享映射MAP_SHARED:多个进程对同一个文件的映射是共享的,一个进程对映射的内存做了修改,另一个进程也会看到这种变化。 (即,我这个进程修改了一个地方,其他进程也看得到)
      • 私有映射MAP_PRIVATE:多个进程对同一个文件的映射不是共享的,一个进程对映射的内存做了修改,另一个进程并不会看到这种变化,也不会真的写到文件中去。
    • 参数5:文件操作符

    • 参数6:偏移量

  • 返回值:如果mmap成功则返回映射首地址,如果出错则返回常数MAP_FAILED

mmap的注意事项:

1、用于进程之间通信,一般设计成结构体,用于传输数据

2、进程间通信,一般设计成临时文件(读完删除使用unlink)

3、当有总线出错,优先查看共享文件是否有存储空间

4、如果段出错可能是使用到了,还没创建的文件(或者是不存在的文件)

示例文件

写文件端

#include
#include 
#include
#include
#include 
#include 
#include 
#include 
#define MAPLEN 0x1000	//这个我来定义文件的大小,一会要映射到内存

//定义一个学生的结构体
struct STU 
{
	int ID;
	char name[64];
	char sex;
};

//定义一个函数,专门来输出进程执行时的错误
void sys_err(char *str,int exitno)
{
	perror(str);
	exit(exitno);
}

/*
你要先要创建一个hello文件,不然一会执行的时候会报段出错
使用mmap来修改文件信息
*/

//文件写成传参的形式
int main(int argc,char *argv[])
{
	//创建一个文件结构体变量
	struct STU *mm;
	int fd,i=0;

 	if(argc < 2)
	{
		printf("enter your name\n");
		exit(1);
	}
	
	//执行时输入的文件名,会在这里打开
	fd = open(argv[1],O_RDWR|O_CREAT,0777);
 	
 	//如果打开失败
	if(fd<0)
	{
		//报错
		sys_err("open",1);
	}
	
	//写入一个“\0”,来扩展文件,让文件由存储空间,不写这下面两个if会报总线错误
	if(lseek(fd,MAPLEN-1,SEEK_SET)<0)
	{
		sys_err("mmsp",3);
	}
	if(write(fd,"\0",1)<0)
	{
		sys_err("write",4);
	}
	//MAPLEN是我上面的宏定义
	mm = mmap(NULL,MAPLEN,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
	
	if(mm == MAP_FAILED)
	{
		sys_err("mmsp",2);
	}
	close(fd);
	
	while(1)
	{
		mm->ID = i;	
 		sprintf(mm->name,"zhaozhao - %d",i++);
		 if(i%2 == 0)
	 	{
	 		mm->sex = 'm';
	 	}
	 	else
	 	{
	 		mm->sex = 'w';
	 	}
	 	sleep(1);
	}
	
	//关闭映射
	munmap(mm,MAPLEN);
	 
	
	return 0;

读文件端

#include
#include 
#include
#include
#include 
#include 
#include 
#include 
#define MAPLEN 0x1000
struct STU 
{
	int ID;
	char name[64];	
	char sex;
};

void sys_err(char *str,int exitno)
{
	perror(str);
	exit(exitno);
}


int main(int argc,char *argv[])
{
	struct STU *mm;
	int fd,i=0;
 
	if(argc < 2)
	{
	printf("enter your name\n");
	exit(1);
 	}
 	
 	//打开文件,只读模式
 	fd = open(argv[1],O_RDWR);
 
 	if(fd < 0)
 	{
  		sys_err("open",1);
 	}

	mm = mmap(NULL,MAPLEN,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
 
 	if(mm == MAP_FAILED)
 	{
  		sys_err("mmsp",2);
 	}
 	close(fd);
	
	//读完删除文件,防止创建多个文件
 	nulink(argv[1]);
	while(1)
 	{
  		printf("%d\n",mm->ID);
  		printf("%s\n",mm->name);
  		printf("%c\n",mm->sex);
  		sleep(1);
 	}
	//关闭
 	munmap(mm,MAPLEN); 
 	return 0;
}

在写端执行文件的的时候,输入文件名称(没有创建过的文件),在执行读端的时候输入参数(前面创建的文件名称)这样在终端上就能看见读取的数据。

你可能感兴趣的:(Linux)