【前言】对这两个理解还是不够深刻,写一篇博客来记录一下。

首先关于共享内存的链接:共享内存里面包含了创建共享内存区域的函数,以及两个进程怎么挂载共享内存通信,分离、释放共享内存。

共享内存的好处就是效率高,不需要太多次的进行数据的 copy。可以直接进行读写内存。所以,相对来说在 IPC 进程间通信三大主题里面,共享内存要比消息队列使用多,而且消息队列只在有血缘关系的进程间通信;但是,共享内存不保证同步,使用了信号量用来保证共享内存同步。Linux 中的两种共享内存。一种是我们的 IPC 通信 System V 版本的共享内存,另外的一种就是我们今天提到的存储映射 I/O(mmap 函数),当然还有一种 POSIX 的共享内存,它是在 mmap 基础之上构建的。

一、mmap

mmap I/O 的描述符间接说明内存映射是对文件操作。另外,mmap 另外可以在无亲缘的进程之间提供共享内存区。这样,类似的两个进程之间就是可以进行了通信。

Linux 提供了内存映射函数 mmap, 它把文件内容映射到一段内存上 (准确说是虚拟内存上,运行着进程), 通过对这段内存的读取和修改, 实现对文件的读取和修改。 mmap() 系统调用使得进程之间可以通过映射一个普通的文件实现共享内存。普通文件映射到进程地址空间后,进程可以像访问内存的方式对文件进行访问,不需要其他内核态的系统调用 (read,write) 去操作。

这里是讲设备或者硬盘存储的一块空间映射到物理内存,然后操作这块物理内存就是在操作实际的硬盘空间,不需要经过内核态传递。比如你的硬盘上有一个文件,你可以使用 linux 系统提供的 mmap 接口,将这个文件映射到进程一块虚拟地址空间,这块空间会对应一块物理内存,当你读写这块物理空间的时候,就是在读取实际的磁盘文件,就是这么直接高效。通常诸如共享库的加载都是通过内存映射的方式加载到物理内存的

mmap 系统调用并不完全是为了共享内存来设计的,它本身提供了不同于一般对普通文件的访问的方式,进程可以像读写内存一样对普通文件进行操作,IPC 的共享内存是纯粹为了共享。

1、mmap 系统调用介绍:

void mmap(void addr, size_t length, int prot, int flags, int fd, off_t offset);

这就是 mmap 系统调用的接口,mmap 函数成功返回指向内存区域的指针,失败返回 MAP_FAILED。

addr,某个特定的地址作为起始地址,当被设置为 NULL,标识系统自动分配地址。实实在在的物理区域。

length 说的是内存段的长度。

prot 是用来设定内存段的访问权限。

prot 参数 说明
PROT_READ 内存段可读
PROT_WRITE 内存段可写
PROT_EXEC 内存段可执行
PROT_NONE 内存段不能被访问

flags 参数控制内存段内容被修改以后程序的行为。

flags 参数 说明
MAP_SHARED 进程间共享内存,对该内存段修改反映到映射文件中。提供了 POSIX 共享内存
MAP_PRIVATE 内存段为调用进程所私有。对该内存段的修改不会反映到映射文件
MAP_ANNOYMOUS 这段内存不是从文件映射而来的。内容被初始化为全 0
MAP_FIXED 内存段必须位于 start 参数指定的地址处,start 必须是页大小的整数倍(4K 整数倍)
MAP_HUGETLB 按照大内存页面来分配内存空间

fd 参数是用来被映射文件对应的文件描述符。通过 open 系统调用得到。
offset 设定从何处进行映射。

2、mmap 用于共享内存的方式

1、我们可以使用普通文件进行提供内存映射,例如,open 系统调用打开一个文件,然后进行 mmap 操作,得到共享内存,这种方式适用于任何进程之间。

2、可以使用特殊文件进行匿名内存映射,这个相对的是具有血缘关系的进程之间,当父进程调用 mmap,然后进行 fork,这样父进程创建的子进程会继承父进程匿名映射后的地址空间,这样,父子进程之间就可以进行通信了。相当于是 mmap 的返回地址此时是父子进程同时来维护。

3、另外 POSIX 版本的共享内存底层也是使用了 mmap。所以,共享内存在在 posix 上一定程度上就是指的内存映射了https://www.cnblogs.com/LubinLew/p/POSIX-shared_memory.html

二、mmap 和 System V 共享内存的比较

共享内存:

共享内存与存储映射(mmap) - 图1

这是 System V 版本的共享内存(以下我们统称为 shm),下面看下 mmap 的:
共享内存与存储映射(mmap) - 图2

总结:

1、mmap 是在磁盘上建立一个文件,每个进程地址空间中开辟出一块空间进行映射。而 shm 共享内存,每个进程最终会映射到同一块物理内存。shm 保存在物理内存,这样读写的速度肯定要比磁盘要快,但是存储量不是特别大。

2、相对于 shm 来说,mmap 更加简单,调用更加方便,所以这也是大家都喜欢用的原因。

3、另外 mmap 有一个好处是当机器重启,因为 mmap 把文件保存在磁盘上,这个文件还保存了操作系统同步的映像,所以 mmap 不会丢失,但是 shmget 在内存里面就会丢失。

4、总之,共享内存是在内存中创建空间,每个进程映射到此处。内存映射是创建一个文件,并且映射到每个进程开辟的空间中。

但在 posix 中的共享内存就是指这种使用文件的方式 “内存映射”。参考:https://www.cnblogs.com/LubinLew/p/POSIX-shared_memory.html

附录:linux 高端内存https://www.cnblogs.com/wuchanming/p/4360277.html
https://www.cnblogs.com/huangfuyuan/p/9476951.html