本文主要说一下共享内存的原理
本文源码来自于 glibc 2.27
一、共享内存的底层实现
我们看一下 shm_open
的源码实现。
1 | int shm_open (const char *name, int oflag, mode_t mode) { |
可以看到 shm_open 的底层其实也是调用的 open 函数,打开的一个文件描述符。其中 shm_name 由宏定义 SHM_GET_NAME
来获取。
1 | #define _PATH_DEV "/dev/" |
从以上的代码,我们可以清晰的看出,shm_name 的组成,一部分是 /dev/shm/
,另外一部分是用户传入的 name。最终一起组成文件的地址 /dev/shm/user_name
。
二、为什么共享内存要放在 /dev/shm
下呢?
/dev/shm
一般是一种特殊的文件系统 tmpfs
。
1 | # df -h |
tmpfs 的解释:
1 | Tmpfs is a file system which keeps all files in virtual memory. |
意思是说,tmpfs 是一个文件系统,所有的文件都存在于虚拟内存中。如果说卸载了 tmpfs 实例,那么存储的内容将全部丢失。
那么如果机器重启,共享内存会丢失,毕竟是内存嘛。
那么创建共享内存的进程要是重启呢?共享内存还会在吗?那么现在产生了一个问题:虚拟内存必须要映射到物理内存,才可以使用,tmpfs 的虚拟内存会映射到哪里呢?
三、“共享内存” 位于哪里?
先来看第一个问题:
1 | Since tmpfs lives completely in the page cache and on swap, all tmpfs |
tmpfs 完全存在于 page cache 和 swap 中,当前在内存中的 tmpfs 会显示为 cached,不会显示为 shared 或其他。可以通过 df 和 du 命令查看 tmpfs 真实使用的物理内存和 swap 的大小。
我们来做实验论证一下。我的机器是 ubuntu22.04
。
我们首先通过
df、free
来查看当前的/dev/shm
占有空间和buffer/Cache
的占用1
2
3
4
5
6
7
8
9# df -h
Filesystem Size Used Avail Use% Mounted on
tmpfs 1.9G 0 1.9G 0% /dev/shm
...
# free -h
total used free shared buff/cache available
Mem: 3.8Gi 998Mi 773Mi 43Mi 2.0Gi 2.5Gi
Swap: 3.6Gi 9.0Mi 3.6Gi我们看到此时
/dev/shm
中没有空间占用,buff/cache
为 2G然后我们在
/dev/shm
中创建一个 100MB 的文件1
# dd if=/dev/zero of=/dev/shm/hello bs=1M count=100
再来查看占用情况
1
2
3
4
5
6
7
8
9df -h
Filesystem Size Used Avail Use% Mounted on
tmpfs 1.9G 100M 1.8G 6% /dev/shm
...
# free -h
total used free shared buff/cache available
Mem: 3.8Gi 992Mi 678Mi 143Mi 2.1Gi 2.4Gi
Swap: 3.6Gi 9.0Mi 3.6Gi我们发现
/dev/shm
此时占用 100M,而buff/cache
增加了 100M。验证了的确/dev/shm
是被创建在buff/cache
中的我们再来测试删除情况
1
2
3
4
5
6
7
8
9# free -m
total used free shared buff/cache available
Mem: 3871 996 668 143 2205 2449
Swap: 3715 9 3706
# rm /dev/shm/hello
# free -m
total used free shared buff/cache available
Mem: 3871 1013 751 43 2105 2532
Swap: 3715 9 3706
至此,我们通过实验论证了 tmpfs 文件系统中内存空间占用,位于 buff/cache
中。
也回答了创建共享内存的进程即使 crash 掉,也不会影响共享内存,因为共享内存存在于操作系统内核的 buff/cache
中。
四、注意
在使用共享内存时,注意要挂载
1 | glibc 2.2 and above expects tmpfs to be mounted at /dev/shm for |
程序员在使用 POSIX 的时候,记得在 /etc/fstab
中编辑一下,把 tmpfs 分区进行挂载。