Linux-内存共享映射

进程间通信-内存共享映射

一、简述

  • mmap()系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以向访问普通内存一样对文件进行访问,不必再调用read(),write()等操作。
  • mmap()的作用是映射文件描述符fd指定文件的 [off,off + len]区域至调用进程的[addr, addr + len]的内存区域, 如下图所示:

Linux-内存共享映射

二、mmap 函数

功能
mmap() 可以把磁盘文件的一部分直接映射到内存,这样文件中的位置直接就有对应的内存
地址,对文件的读写可以直接用指针来做而不需要read/write函数。

头文件

#include <sys/mman.h>

函数原型

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

参数说明

  • addr:指定文件应被映射到进程空间的起始地址,一般被指定一个空指针,此时选择起始地址的任务留给内核来完成。
  • len:是映射到调用进程地址空间的字节数,它从被映射文件开头offset个字节开始算起。
  • prot:指定空想内存的访问权限。可取如下几个值的或:PROT_READ(可读)、PROT_WRITE(可写)、PROT_EXEC(可执行)、PROT_NONE(不可访问)。
  • flag:由以下几个常值指定:MAP_SHARED、MAP_PRIVATE、MAP_FIXED,其中,MAP_SHARED,MAP_PRIVATE必选其一,而MAP_FIXED则不推荐使用。
    • MAP_SHARED:多个进程对同一个文件的映射是共享的,一个进程对映射的内存做了修
      改,另一个进程也会看到这种变化。
    • MAP_PRIVATE:多个进程对同一个文件的映射不是共享的,一个进程对映射的内存做了修
      改,另一个进程并不会看到这种变化,也不会真的写到文件中去。
  • fd:为即将映射到进程空间的文件描述字,一般由open()返回,同时,fd可以指定为-1,此时须指定flags参数中的MAP_ANON,表明进行的是匿名映射(不涉及具体的文件名,避免了文件的创建及打开,很显然只能用于具有亲缘关系的进程间进行通信)。
  • offset:表示偏移量,一般设为0,表示从文件头开始映射。

返回值
函数的返回值为最后文件映射到进程空间的地址,进程可直接操作起始地址为该值的有效地址。

三、应用示例

mmap_w.c

/* process_mmap_w.c*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#define MAPLEN 0x1000
struct STU {
	int id;
	char name[20];
	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("./a.out filename\n");
		exit(1);
	}
	fd = open(argv[1], O_RDWR | O_CREAT, 0777);
	if (fd < 0)
		sys_err("open", 1);
	if (lseek(fd, MAPLEN-1, SEEK_SET) < 0)
		sys_err("lseek", 3);
	if (write(fd, "\0", 1) < 0)
		sys_err("write", 4);
	mm = mmap(NULL, MAPLEN, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
	if (mm == MAP_FAILED)
		sys_err("mmap", 2);
	close(fd);
	while (1) {
		mm->id = i;
		sprintf(mm->name, "zhang-%d", i);
		if (i % 2 == 0)
			mm->sex = 'm';
		else
			mm->sex = 'w';
		i++;
		sleep(1);
	}
	munmap(mm, MAPLEN);
	return 0;
}

mmap_r.c

/* process_mmap_r.c*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#define MAPLEN 0x1000
struct STU {
	int id;
	char name[20];
	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("./a.out filename\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("mmap", 2);
	close(fd);
	unlink(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;
}

运行结果:

进程1:写
Linux-内存共享映射

进程2:读
Linux-内存共享映射

参考来源:
https://blog.****.net/windgs_yf/article/details/81146887