Linux系统编程-进程-存储映射

83 阅读4分钟

进程映射

将磁盘文件映射到内存当中,这样进程间通信就不需要特地用到什么管道了,用普通文件速度一样快

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、PROT_READ|PROT_WRITE
  • flags:标注共享共享内存的共享属性。MAP_SHARED、MAP_PRIVATE
  • fd:用于创建共享内存映射区的那个文件的文件描述符
  • offset:偏移位置,默认0,必须是4k的整数倍

返回值:

  • 成功:映射区的首地址,相当于开辟了内存空间
  • 失败:(void*)-1
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>

void sys_err(const char *str){
        perror(str);
        exit(1);
}

int main(int argc,char* argv[]){
        char* p=NULL;
        int fd;

        fd=open("testmap",O_RDWR|O_CREAT|O_TRUNC,0644);
        if(fd==-1){
                sys_err("open error");
        }

        //拓展文件大小
        ftruncate(fd,10);
        int len=lseek(fd,0,SEEK_END);

        //创建共享内存空间
        p=mmap(NULL,len,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
        if(p==MAP_FAILED){
                sys_err("mmap error");
        }

        //对p进行写操作,人家本来就返回一个指针
        strcpy(p,"hello");

        printf("----%s\n",p);

        return 0;
}

mmap注意事项

  1. 用于创建映射区的文件大小为0,实际指定非0大小创建映射区,“总线错误”
  2. 用于创建映射区的文件大小为0,实际指定0大小创建映射区,“无效参数”
  3. 创建映射区首先要对磁盘文件进行读,所以文件有读权限才能创建映射区。对映射区进行写,如果映射区为共享,那么最后需要写回,需要文件的读权限。如果映射区为私有,最后不会写回,只会对映射区的内容产生影响,那么不需要文件的写权限。
  4. 用于创建映射区的文件读写属性为,只读。映射区属性为,读写。“无效参数”
  5. 文件描述符fd可以在创建完映射区后就关闭,文件描述符只是个链接而已,mmap返回的指针一样能访问。
  6. offset必须是4k的整数倍
  7. 映射区访问权限为PRIVATE,对映射区的修改只在内存上有效,不会写回

使用mmap最保险的方式

  1. fd = open("文件名",O_RDWR);
  2. mmap(NULL,有效文件大小,PROT_READ|PROT_WRITE|MAP_SHARED,fd,0);

父子进程mmap

父进程先创建映射区。open(O_RDWR),mmap(MAP_SHARED)
fork()创建子进程
一个进程写,另一个进程读

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

int var=100;

int main(){
	int *p;
	pid_t pid;
	
	int fd=open("temp",O_CREAT|O_RDWR|O_TRUNC,0644);
	if(fd<0){
		perror("open error");
		exit(1);
	}
	unlink("temp"); //准备删除文件
	ftruncate(fd,4);
	p=(int *)mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
	//p=(int *)mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_PRIVATE,fd,0);  //设为私有,那么fork以后,父子进程各自有一份,互不干扰

	pid=fork();
	if(pid<0){
		perror("pid error");
		exit(1);
	}else if(pid==0){
		*p=2000;
		var=1000;
		printf("child, *p=%d, var=%d\n",*p,var);
	}else if(pid>0){
		wait(NULL);
		printf("parent, *p=%d, var=%d\n",*p,var);
		close(fd);
	}
}

无血缘关系进程mmap

两个进程打开同一个文件,创建映射区。你不用管实现原理,反正打开相同文件就是能映射到相同的映射区。
指定flags为MAP_SHARE
一个进程写入,一个进程读出。

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

struct student{
	int id;
	char name[26];
	int age;
};

void sys_err(const char* str){
	perror(str);
	exit(1);
}

int main(int argc,char* argv[]){
	int fd;
	struct student *p;
	struct student stu={18,"xiaoming",10};
	fd=open("test_map",O_CREAT|O_RDWR|O_TRUNC,0644);
	if(fd==-1){
		sys_err("open error");
	}
	
	ftruncate(fd,sizeof(stu));
	p=mmap(NULL,sizeof(stu),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
	if(p==MAP_FAILED){
		sys_err("mmap error");
	}
	
	close(fd);
	while(1){
		printf("student name=%s, id=%d, age=%d\n",p->name,p->id,p->age);
		sleep(1);
	}
	munmap(p,sizeof(stu));

}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>

struct student{
	int id;
	char name[26];
	int age;
};

void sys_err(const char* str){
	perror(str);
	exit(1);
}

int main(int argc,char* argv[]){
	struct student stu={1,"xiaoming",18};
	struct student *p;
	int fd=open("test_map",O_CREAT|O_RDWR|O_TRUNC,0644);
	if(fd==-1){
		sys_err("open error");
	}
	
	ftruncate(fd,sizeof(stu));
	p=mmap(NULL,sizeof(int),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
	if(p==MAP_FAILED){
		sys_err("mmap error");
	}

	while(1){
		memcpy(p,&stu,sizeof(stu));
		stu.id++;
		sleep(1);
	}
	munmap(p,sizeof(stu));

	close(fd);
}