前言
进程间通信是操作系统中重要的概念之一,使得不同的进程可以相互交换数据和进行协作。其中,共享内存是一种高效的进程间通信机制,而内存映射(mmap)是实现共享内存的一种常见方法。
一、存储映射 I/O
存储映射 I/O 是 一个磁盘文件 与 存储空间中的一个缓冲区相映射。于是, 当从缓冲区中取数据,就相当于读文件中的相应字节。于此类似,将数据存入缓冲区,则相应的字节就自动写入文件。这样, 就可在 不适用 read 和 write 函数的情况下,使用 地址(指针)完成 i/o 操作。
使用这种方法,首先应通知内核,将一个指定文件映射到存储区域中。这个映射工作可以通过 mmap 函数来实现。使用 mmap 系统调用,进程可以直接操作共享内存的指针,而不需要复杂的数据结构和同步机制。
理解 共享内存:
共享内存是一种特殊的内存区域,它可以被多个进程访问和操作。这意味着不同的进程可以直接读取或写入该共享内存区域中的数据。相比于其他进程间通信机制,共享内存具有较低的开销和高效的数据传输速度。
二、mmap, munmap
mmap 用于创建共享内存映射。munmap 用来 释放内存。
#include <sys/mman.h> void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); int munmap(void *addr, size_t length);
- void * mmap ( void * addr, size_t length , int prot , int flags , int fd , off_t offset ) ;
参数:
- addr : 指定映射区的首地址。通常传 NULL / 0,表示让系统自动分配。
- length :共享映射区的大小。
- prot : 共享映射区的读写属性。
- flags : 标注共享内存的共享属性。
- fd :用于创建共享映射区的哪个文件的,文件描述符。
- offset :
返回值:
- 成功 : 映射区的首地址。
- 失败 : 返回 M AP_FAILED。
- int munmap ( void * addr , size_t length ) ;
三、父子进程间 mmap 通信
void sys_error(const char *str) { perror(str); exit(1); // 正常退出程序 } int var = 10; int main(void) { int fd; char *p; pid_t pid; fd = open("1.txt", O_RDWR); if(fd < 0) { sys_error("open error"); } ftruncate(fd,100); // 扩展空间大小 int len = lseek(fd,0,SEEK_END); p = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED,fd, 0); // 创建共享映射区 if(p == MAP_FAILED) { sys_error("map error!"); } close(fd); pid = fork(); // 创建子进程 if(pid == 0) { strcpy(p,"This is child"); var = 100; printf("%s, Child: var = %d\n",p, var); } else { sleep(1); printf("Parent: %s,var = %d\n",p,var); } wait(NULL); // 回收子进程 munmap(p, len); // 释放映射区 return 0; }
var 是 全局变量,父子进程操作 全局变量时,读数据时 共享; 写数据时 复制。
上述代码中,子进程写数据时,是复制一份数据 后 对复制的数据进程修改。父进程 读数据时,全局变量还是原本的数值。
四、非血缘关系进程间 mmap 提通信
1.c 不断写数据:
void sys_error(const char *str) { perror(str); exit(1); } int main(void) { int fd; char *p; int i = 0; fd = open("1.txt", O_RDWR); if(fd < 0) { sys_error("open error"); } ftruncate(fd,100); int len = lseek(fd,0,SEEK_END); p = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED,fd, 0); if(p == MAP_FAILED) { sys_error("map error!"); } close(fd); while(1) { sleep(1); *p = i; // 不断写入数据 i++; } munmap(p, len); return 0; }
2.c 不断读数据
void sys_error(const char *str) { perror(str); exit(1); } int main(void) { int fd; char *p; int i = 0; fd = open("1.txt", O_RDWR); if(fd < 0) { sys_error("open error"); } ftruncate(fd,100); int len = lseek(fd,0,SEEK_END); p = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED,fd, 0); if(p == MAP_FAILED) { sys_error("map error!"); } close(fd); while(1) { sleep(1); printf("*p = %d\n",*p); // 不断读数据 } munmap(p, len); return 0; }
五、mmap 匿名映射区
mmap 匿名映射区是在进程的虚拟内存空间中创建的一段没有对应物理文件的内存区域。它通常用于进程间通信和临时存储数据,不需要使用文件作为映射源。匿名映射区在 Linux 系统中非常常见。
在使用mmap系统调用创建匿名映射区时,传递给mmap函数的文件描述符参数(通常为-1)表明不会有一个与之相关联的文件。
mmap 函数的 参数二,可以为指定的大小。参数四 为 MAP_SHARED|MAP_ANONYMOUS。
void sys_error(const char *str) { perror(str); exit(1); // 正常退出程序 } int var = 10; int main(void) { char *p; pid_t pid; p = mmap(NULL, 20, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); // 创建共享映射区 if(p == MAP_FAILED) { sys_error("map error!"); } pid = fork(); // 创建子进程 if(pid == 0) { strcpy(p,"This is child"); var = 100; printf("%s, Child: var = %d\n",p, var); } else { sleep(1); printf("Parent: %s,var = %d\n",p,var); } wait(NULL); // 回收子进程 munmap(p, 20); // 释放映射区 return 0; }
总结
进程间共享内存映射(mmap)通信是一种高效、灵活的进程间通信机制。通过内存映射,不同的进程可以共享相同的数据区域,提高数据访问速度和性能。然而,在使用该机制时需要注意同步机制、内存管理和安全性等问题,以确保共享数据的正确性和安全性。