整体认知
共享内存是一种读写文件的方式。
如下图所示:
注:暂不需要关注共享内存的实现机制
和共享内存相关的函数有: map, unmap
mmap ,将文件映射到内存
函数原型是:
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
参数说明:
addr:内存映射区开始地址的建议值。一般将这个值设为NULL,让内核来分配地址。如果手动指定了一个值,那么内核会参考这个值。length:内存映射的长度,必须是内存页大小的整数倍。(可以通过sysconf(SC_PAGE_SIZE)得到页尺寸)port:对映射内存的保护,有:可执行PROT_EXEC,可读PROT_READ,可写PROT_WRITE,不可访问PROT_NONE。注意:该参数不能与文件的打开模式有冲突flag:映射内存的更新是否对其他进程可见,更新是否要写回到文件。MAP_SHARED表示对其他进程可见,且写回到文件。
返回值:
- 成功:返回内存映射区的开始地址。
- 失败:返回
(void *) -1,并且设置对应的errno
注意:
mmap函数返回之后,fd可以立即关闭,不会取消引用。
映射区的可访问性和写回文件
- 映射时填入的大小
length和文件的实际大小无关 - 对内存映射区的读写操作不会改变文件的大小,写回不会超过文件的大小
- 可访问的内存映射区和文件的当前大小有关,可访问区域的大小为:
用一张图来表示:可访问的映射区大小 = (region + page_size - 1) & ~(page_size - 1)

unmap 函数:取消内存映射
函数原型为:
int munmap(void *addr, size_t length);
参数说明:
addr:必须时页尺寸的整数倍length:。。。
注:进程退出也会自动解除内存映射
使用示例
这个例子是这样的:一共有两个进程: reader, writer 。 writer 向共享内存写入内容,reader 每隔一秒打印共享内存的内容。
reader 的代码:
#include <sys/mman.h>#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <string.h>#include <sys/stat.h>#include <fcntl.h>/* 内存映射的文件名称 */#define SHARED_FILE "share"/*** 内存映射区的数据结构*/typedef struct share {char key[256];char val[256];} share_t;int main(int argc, char *argv[]){int fd;share_t *share_ptr = NULL;// 打开文件fd = open(SHARED_FILE, O_RDONLY);if (fd < 0)fprintf(stderr, "open file failed: %s\n", SHARED_FILE);// 设置内存映射share_ptr = mmap(NULL, sizeof(share_t), PROT_READ, MAP_SHARED, fd, 0);// 可以关闭文件了close(fd);// 从内存映射读取printf("mmap content is: \n");while(1) {printf("[key: %20s, value: %20s]\n", share_ptr->key, share_ptr->val);sleep(1);}munmap(share_ptr, sizeof(share_t));return 0;}
writer 的代码:
#include <sys/mman.h>#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <string.h>#include <sys/stat.h>#include <fcntl.h>#include "share.h"int main(int argc, char *argv[]){int share_fd;void *temp = NULL;share_t *share_ptr = NULL;/* 打开文件 */share_fd = open(SHARED_FILE, O_RDWR | O_CREAT | O_TRUNC);if (share_fd < 0)fprintf(stderr, "open file failed: %s\n", SHARED_FILE);/* 填充文件的长度 */temp = malloc(sizeof(share_t));if (!temp) {fprintf(stderr, "malloc(%ld) failed.\n", sizeof(share_t));return 0;}memset(temp, 0, sizeof(share_t));write(share_fd, temp, sizeof(share_t));free(temp);/* 设置内存映射 */share_ptr = mmap(NULL, sizeof(share_t),PROT_READ | PROT_WRITE, MAP_SHARED, share_fd, 0);/* 可以关闭文件了 */close(share_fd);/* 向内存映射区写东西 */while(1) {printf("input key: \n");scanf("%s", share_ptr->key);printf("input value: \n");scanf("%s", share_ptr->val);}munmap(share_ptr, sizeof(share_t));return 0;}
附注
参考资料:
- 《认真分析mmap:是什么 为什么 怎么用》https://www.cnblogs.com/huxiao-tee/p/4660352.html
fedora32: man mmap
文档材料
共享内存.drawio
