整体认知

共享内存是一种读写文件的方式。
如下图所示:
image.png

注:暂不需要关注共享内存的实现机制

和共享内存相关的函数有: map, unmap

mmap ,将文件映射到内存

函数原型是:

  1. 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 和文件的实际大小无关
  • 对内存映射区的读写操作不会改变文件的大小,写回不会超过文件的大小
  • 可访问的内存映射区和文件的当前大小有关,可访问区域的大小为:
    1. 可访问的映射区大小 = (region + page_size - 1) & ~(page_size - 1)
    用一张图来表示:
    image.png

unmap 函数:取消内存映射

函数原型为:

  1. int munmap(void *addr, size_t length);

参数说明:

  • addr :必须时页尺寸的整数倍
  • length :。。。

注:进程退出也会自动解除内存映射

使用示例

这个例子是这样的:一共有两个进程: reader, writerwriter 向共享内存写入内容,reader 每隔一秒打印共享内存的内容。

reader 的代码:

  1. #include <sys/mman.h>
  2. #include <stdio.h>
  3. #include <unistd.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include <sys/stat.h>
  7. #include <fcntl.h>
  8. /* 内存映射的文件名称 */
  9. #define SHARED_FILE "share"
  10. /**
  11. * 内存映射区的数据结构
  12. */
  13. typedef struct share {
  14. char key[256];
  15. char val[256];
  16. } share_t;
  17. int main(int argc, char *argv[])
  18. {
  19. int fd;
  20. share_t *share_ptr = NULL;
  21. // 打开文件
  22. fd = open(SHARED_FILE, O_RDONLY);
  23. if (fd < 0)
  24. fprintf(stderr, "open file failed: %s\n", SHARED_FILE);
  25. // 设置内存映射
  26. share_ptr = mmap(NULL, sizeof(share_t), PROT_READ, MAP_SHARED, fd, 0);
  27. // 可以关闭文件了
  28. close(fd);
  29. // 从内存映射读取
  30. printf("mmap content is: \n");
  31. while(1) {
  32. printf("[key: %20s, value: %20s]\n", share_ptr->key, share_ptr->val);
  33. sleep(1);
  34. }
  35. munmap(share_ptr, sizeof(share_t));
  36. return 0;
  37. }

writer 的代码:

  1. #include <sys/mman.h>
  2. #include <stdio.h>
  3. #include <unistd.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include <sys/stat.h>
  7. #include <fcntl.h>
  8. #include "share.h"
  9. int main(int argc, char *argv[])
  10. {
  11. int share_fd;
  12. void *temp = NULL;
  13. share_t *share_ptr = NULL;
  14. /* 打开文件 */
  15. share_fd = open(SHARED_FILE, O_RDWR | O_CREAT | O_TRUNC);
  16. if (share_fd < 0)
  17. fprintf(stderr, "open file failed: %s\n", SHARED_FILE);
  18. /* 填充文件的长度 */
  19. temp = malloc(sizeof(share_t));
  20. if (!temp) {
  21. fprintf(stderr, "malloc(%ld) failed.\n", sizeof(share_t));
  22. return 0;
  23. }
  24. memset(temp, 0, sizeof(share_t));
  25. write(share_fd, temp, sizeof(share_t));
  26. free(temp);
  27. /* 设置内存映射 */
  28. share_ptr = mmap(NULL, sizeof(share_t),
  29. PROT_READ | PROT_WRITE, MAP_SHARED, share_fd, 0);
  30. /* 可以关闭文件了 */
  31. close(share_fd);
  32. /* 向内存映射区写东西 */
  33. while(1) {
  34. printf("input key: \n");
  35. scanf("%s", share_ptr->key);
  36. printf("input value: \n");
  37. scanf("%s", share_ptr->val);
  38. }
  39. munmap(share_ptr, sizeof(share_t));
  40. return 0;
  41. }

附注

参考资料:

文档材料
共享内存.drawio