资讯动态

深入理解 /dev/shm:tmpfs 内存文件系统原理、调优与实战排坑

发布时间:2026/9/29 5:53:24 来源:尧图企业网站定制
1. 走近 /dev/shmLinux 里的“隐形内存盘”接触 Linux 时间久了你会发现系统里有不少“奇怪”的目录/dev/shm 就是其中之一。第一次注意到它多半是在执行 df -h 的时候突然看到一行 tmpfs 挂载在 /dev/shm 上大小还是物理内存的一半既不是磁盘分区也不是常见的 ext4 或 xfs。当时我的第一反应是这玩意儿到底是什么为什么会占着内存简单来说/dev/shm 是 Linux 内核提供的一个基于 tmpfs 的共享内存目录。它不是一个真实的磁盘分区而是把一块内存划分出来以文件系统的形式暴露给用户。你往 /dev/shm 里写文件实际上是往内存里写数据你从 /dev/shm 里读文件实际上是直接从内存里读。整个过程不经过磁盘 I/O速度自然快得离谱。这个目录的价值主要体现在三个方面第一它是 POSIX 共享内存的默认挂载点很多依赖共享内存进行进程间通信IPC的应用程序会默认使用它第二它给用户提供了一个可以“像操作文件一样操作内存”的入口第三它的默认大小通常是物理内存的一半可以随系统动态调整。对于运维工程师、系统管理员、以及日常需要折腾 Linux 的开发人员来说理解和掌握 /dev/shm 是非常实用的一项技能。这篇内容会从原理讲到实操再讲到排坑帮你彻底搞懂它并且学会如何安全地修改它的大小。2. 深度拆解tmpfs 到底是什么为什么它这么快2.1 tmpfs 的内核原理tmpfs 的全称是 Temporary File System它和 ramfs 同属“内存文件系统”家族。ramfs 是早期的实现直接把文件放进物理内存但有个致命问题——它会无限增长把内存吃完。tmpfs 在 ramfs 的基础上加入了容量限制、swap 支持等机制更安全也更可控。从内核的角度看tmpfs 使用了 Linux 内核的 page cache 和 dentry 缓存机制。当你创建一个 tmpfs 文件时内核在内存中分配相应的页面并将文件系统的元数据也保存在内存里。因此tmpfs 的读写不需要经过块设备层不需要磁盘寻址也不需要等待机械硬盘或 SSD 的 FTL 映射延迟非常低。tmpfs 还有一个特性是“按需分配”。它虽然规定了文件系统最大可以占用多少内存但并不是说一开始就会占满。只有当你真正往里面写数据时内存才会被占用删掉文件后内存立即释放。这一点非常像“用多少占多少”不会白白浪费资源。2.2 为什么 /dev/shm 的读写性能远高于磁盘拿数字来感受一下一块常见的企业级 NVMe SSD顺序读写能做到 3000MB/s 以上随机读写也能做到 1000MB/s 左右这已经是相当不错的成绩了。但在 tmpfs 面前这个数字还是不够看。内存的读写速度通常能达到 10GB/s 到 50GB/s 甚至更高而且访问延迟是纳秒级别的磁盘再怎么优化也比不了。另外一个关键点在于tmpfs 的读写不需要进行“数据复制”到内核缓冲区这一步。普通磁盘文件读取时数据要先从磁盘拷到内核的 page cache再从内核拷到用户空间中间至少经历两次拷贝。tmpfs 虽然也经过系统调用但数据一直在内存里拷贝路径更短CPU 开销更小。我实测过一次很典型的对比在 /dev/shm 里写入一个 1GB 的文件耗时不到 0.3 秒在 xfs 文件系统上执行同样的操作耗时接近 3 秒。如果做大量小文件的随机读写测试差距更大tmpfs 的速度优势可以说碾压级别的。2.3 tmpfs 与普通文件系统的关键区别很多人会把 tmpfs 和“磁盘上的临时目录”弄混比如 /tmp。这里要理清一个概念/tmp 只是约定俗成的临时文件目录文件系统可能挂在普通磁盘上内容重启后也不一定会清空。而 /dev/shm 默认就是 tmpfs数据只存在于内存中重启后一定会丢失因为它没有持久化存储的载体。tmpfs 和普通文件系统还有一个本质区别——空间计算方式。你在普通磁盘上创建一个文件看到的是“磁盘占用”在 tmpfs 里创建文件看到的是“内存占用”。这意味着如果 /dev/shm 被写满系统可用的内存会变少严重时可能触发 OOMOut Of Memory影响其他进程。这是一个非常容易踩的坑后面我会单独展开。3. 实用场景分析谁在使用 /dev/shm为什么要调大小3.1 系统自带的“隐藏消费者”Linux 系统里/dev/shm 并不是一个摆设。最典型的例子是 systemd 旗下的 journald 日志服务。当系统日志量很大的时候journald 会将一部分日志暂存在内存中而默认的挂载点就是 /dev/shm。如果 /dev/shm 空间不足journald 可能会报错日志无法正常写入。另外很多图形界面的应用也会使用共享内存来传递图像数据。比如 Chromium 系浏览器、Electron 应用、以及一些视频播放器都会通过 /dev/shm 来缓存渲染数据。这也是为什么在容器环境中如果不调整 /dev/shm 的大小Chrome 或 Puppeteer 经常会报出 “Could not create shared memory segment” 之类的错误。Docker 容器默认继承宿主机的 /dev/shm但实际上容器内的共享内存是有限的很容易被这类应用撑爆。3.2 业务场景中的典型需求真正让 /dev/shm 进入大众视野的是数据库领域。最经典的是 Oracle 数据库。Oracle 的 SGASystem Global Area和 PGA 在 Linux 上大量使用共享内存默认就是走 /dev/shm。如果 /dev/shm 空间不够Oracle 实例可能无法启动或者运行中频繁报错提示无法分配共享内存段。除了 OraclePostgreSQL 在某些配置下也会使用共享内存。虽然 PG 更多使用的是动态共享内存DSM但部分版本和扩展比如 postgres_fdw 的并行查询依然会依赖 /dev/shm。容器平台如 Kubernetes、Docker里跑 Spark、Presto 这类大数据组件时shuffle 过程也会产生大量临时数据默认落在 /dev/shm 里。很多同学在 K8s 环境里跑 Spark 作业莫名其妙地报“No space left on device”排查了半天才发现是 /dev/shm 满了。3.3 什么情况下需要修改大小跑数据库或大数据组件默认的“物理内存的一半”不够用在容器里跑浏览器自动化或 Electron 程序共享内存段被占满自研程序需要大量临时内存文件希望借助 tmpfs 的性能服务器物理内存很大但业务只需要很小的共享内存可以缩小以释放内存有多个服务同时竞争共享内存需要理性分配容量在这些场景下修改 /dev/shm 的大小就成了一项必需的操作。4. 实操之前先学会查看 /dev/shm 的当前状态4.1 查看大小和使用率在动手修改之前先看看当前是什么状态。最常用的命令是 df -hdf -h /dev/shm执行后大概率会看到类似下面的输出文件系统 容量 已用 可用 已用% 挂载点 tmpfs 7.8G 0 7.8G 0% /dev/shm容量显示 7.8G说明这台机器的物理内存大约是 16G默认 tmpfs 的大小是物理内存的一半。已用Use%显示 0%说明目前还没有什么东西往里写数据。如果你想看更精确的数值用 df -k 或 df -m 输出以 KB 或 MB 为单位的数字方便脚本处理。也可以加 -T 参数查看文件系统类型确认它是不是 tmpfs。4.2 查看挂载参数df 能看到“是什么”但看不到“怎么挂的”。真正要了解挂载参数需要看 /proc/mounts 或者直接执行 mount 命令进行过滤mount | grep /dev/shm输出一般长这样tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev,noexec,relatime,size7979692k,inode64)这里 size7979692k 就表示当前 tmpfs 的最大容量单位是 KB换算下来约等于 7.8GB。注意这里可能还有些细节比如 noexec 表示不能执行此目录下的二进制文件这是系统默认的安全策略。修改大小时如果不小心把 noexec 选项弄丢了会带来安全隐患。4.3 真实内存占用情况光看 df 还不够因为 tmpfs 占用的内存会直接影响系统的可用内存。所以最好再配合 free 命令一起看free -h通过对比 df 显示的 tmpfs 用量和 free 显示的 used 内存你能大概判断出内存被谁吃掉了。如果 df 显示 /dev/shm 已经用了 5GB但 free 显示系统内存确实少了对应的一部分那就说明共享内存确实有数据在占用。这里有个容易混淆的点很多同学以为 /dev/shm 占用的是“缓存”cache但实际上 tmpfs 占用的内存既算在 used 里也可能算在 cache 里它和普通文件页缓存不同是无法被系统自动回收的。这一点和普通磁盘文件的页缓存有本质区别普通文件的缓存可以被回收tmpfs 里的数据一旦写入只要不被删除内存就会一直被占用。5. 修改 /dev/shm 大小的完整实操从临时调整到永久生效5.1 临时调整方案一行 remount 命令快速搞定如果你只是临时想扩容不期望重启后生效最简单的方式是使用 mount 命令重新挂载sudo mount -o remount,size16G /dev/shm执行这个命令后/dev/shm 的容量会立刻变成 16GB。用 df -h /dev/shm 验证一下结果应该已经变了。为什么这个命令能生效因为 /dev/shm 本身就是 tmpfs 挂载点tmpfs 支持重新挂载remount内核会读取新的 size 参数并重新计算可用容量。整个过程是不需要重启任何服务的非常方便。如果以后想改回来同样执行sudo mount -o remount,size默认值 /dev/shm把 size 改成原来的数值就行。不过要注意用 remount 方式修改的大小在系统重启后会失效因为系统会根据 /etc/fstab 里的配置重新挂载 tmpfs。如果你的系统根本没有在 /etc/fstab 里配置 /dev/shm则重启后恢复成默认的“物理内存一半”。5.2 永久调整方案修改 /etc/fstab 实现重启不丢想要永久生效就要修改 /etc/fstab 文件。在文件末尾追加或修改如下内容tmpfs /dev/shm tmpfs defaults,size16G 0 0然后执行sudo mount -amount -a 会根据 /etc/fstab 重新挂载所有条目如果配置没问题/dev/shm 会立即变成 16GB。这里有个经验在修改 /etc/fstab 之前最好先备份一份原文件比如 cp /etc/fstab /etc/fstab.bak。因为 fstab 写错会导致系统无法正常挂载分区严重的话甚至开不了机。为了验证 fstab 配置是否正确可以在重启之前先执行 mount -a如果没有任何报错说明写入的配置能被系统正常解析。此外建议用 findmnt /dev/shm 来查看实际的挂载来源确认是不是走 fstab 加载的。一般输出会明确显示 SOURCE 是 tmpfs、TARGET 是 /dev/shm、OPTIONS 里能看到 size16G。5.3 调整后的验证与回滚修改大小后至少要做三件事验证# 1. 查看容量是否生效 df -h /dev/shm # 2. 查看挂载参数是否包含预期的 size mount | grep /dev/shm # 3. 实际写入文件测试 dd if/dev/zero of/dev/shm/testfile bs1M count1024dd 命令会向 /dev/shm 写入一个 1GB 的测试文件。如果写入过程没有报错说明空间充足写入完成后记得把它删掉rm -f /dev/shm/testfile如果你怀疑配置有问题需要回滚直接编辑 /etc/fstab 删除或注释掉刚才加的那一行再执行 mount -a 即可。临时 remount 的状态不受 fstab 回滚影响但如果重启了系统没有 fstab 配置时就会回到默认大小。5.4 容器环境里的特殊处理Docker 默认情况下容器内的 /dev/shm 大小是 64MB这个值非常小很多程序一跑就爆。你可以用 Docker 参数指定大小docker run -it --shm-size2g ubuntu bashKubernetes 里也有对应的做法。虽然 Pod 级别的 /dev/shm 设置比较复杂但在 Pod 的 YAML 中可以通过 emptyDir 配合 medium: Memory 来实现类似“内存目录”的效果并且可以通过 sizeLimit 限定大小volumes: - name: dshm emptyDir: medium: Memory sizeLimit: 2Gi把 volume 挂载到容器内的 /dev/shm就能实现类似 Docker --shm-size 的效果。需要注意K8s 中 emptyDir 的 sizeLimit 是硬限制超过之后kubelet 会尝试驱逐 Pod 或者停止写入设置的时候要根据实际业务合理分配。6. 实战中的常见问题与排查技巧实录6.1 问题写文件时报 “No space left on device”这个是最常见的报错之一。看到这个错误第一反应可能是磁盘满了但 df -h 一看根分区还剩好多 GB为什么还会报空间不足此时要立刻想到 /dev/shm。执行 df -h /dev/shm如果容量已经满了说明确实是共享内存空间被写满。比如某次我在测试环境跑一个 Java 应用应用使用 off-heap 内存方式写临时文件路径就指向 /dev/shm结果写了几个 GB 后直接报错。当时排查思路是先 df 看 /dev/shm确认满了再 lsof L1 找占用共享内存文件的进程最后定位到问题。具体排查占用文件的命令lsof L1它会列出所有“已被删除但仍被占用”的文件。由于 tmpfs 里的文件删除了但进程还持有文件描述符的话空间不会立即释放。lsof L1 可以将这些进程找出来决定是等待还是 kill 掉。6.2 问题重启后 /dev/shm 大小又变回去了如果你用 remount 方式调整了大小重启之后发现又恢复成物理内存的一半不要慌这是正常现象。因为 remount 只是临时改变当前运行状态没有写进 /etc/fstab。排查思路很简单先 cat /etc/fstab 看看有没有相关配置如果没有果断加上如果有但没生效看看语法有没有写错比如大小写、逗号分隔符、设备名等。还有一种情况是 systemd 的某些版本会在内存中维护自己的挂载配置覆盖 fstab 的行为但这种情况比较少见主要发生在一些定制化比较深的企业发行版上。6.3 问题/dev/shm 满了但 free 显示内存还有很多这种情况多见于开启了 swap 的机器。tmpfs 里的数据可以被换出swapped out所以即使物理内存快满了free 显示的内存依然“够用”但实际上系统已经在频繁换页性能会大幅下降。处理方式是先看 free -h 输出中 swap 的 used 列如果这个值一直在增长说明系统正在把 tmpfs 的数据往 swap 里搬。此时应该考虑扩大 /dev/shm 的 size或者减少写入 /dev/shm 的数据量。同时要警惕如果 swap 也满了系统可能会 OOM甚至触发内核杀死进程。6.4 问题/dev/shm 里的文件重启后神秘消失这不是问题这是特性。tmpfs 本身就是“临时文件系统”内容不落盘重启后自动清空。很多新手第一次遇到会以为是数据丢失了实际上从设计之初就是这样。如果你有数据要持久化保存千万不要放在 /dev/shm应该放到普通磁盘文件系统上。有个折中方案是把 /dev/shm 当成“中间存储”来用比如程序跑完后再把结果写到磁盘持久化这样既能享受内存速度又不会丢数据。6.5 一个容易被忽略的坑tmpfs 的 noexec 选项系统默认挂载 /dev/shm 时会带 noexec 选项意味着你不能直接执行 /dev/shm 目录下的可执行文件。这在某些场景下会坑人。举个例子我之前用某些安装脚本脚本内部会解压二进制文件到临时目录并执行而临时目录恰好指向 /dev/shm结果一直报 “Permission denied”。排查了很久才发现是 noexec 导致的。解决方法是把 TMPDIR 环境变量改成其他目录或者放弃从 /dev/shm 执行程序。如果你确实需要在这个目录执行二进制文件可以考虑挂载时去掉 noexec 选项但在生产环境强烈不建议这样做这会引入安全风险。6.6 排查思路速查表症状可能原因排查命令解决方式No space left on device/dev/shm 满了df -h /dev/shm清理文件或调大 size容器内 Chrome 崩溃容器 /dev/shm 太小docker exec 容器名 df -h /dev/shm加 --shm-size 参数重启后大小恢复默认没有写入 /etc/fstabcat /etc/fstab追加配置并 mount -a无法执行 /dev/shm 下程序noexec 挂载选项mount | grep /dev/shm换路径或去掉 noexec不建议系统 OOM 被杀死tmpfs 写入过多free -h, dmesg降低 tmpfs 使用量或调整 swap7. 一个可参考的完整案例给 32G 内存服务器调整 /dev/shm 大小这里分享一个我实际处理过的案例。有一台 32G 内存的服务器跑着一个 Oracle 数据库和几个 Java 微服务默认 /dev/shm 大小是 16G。运行一段时间后Oracle 告警日志频繁出现共享内存分配失败Java 服务也会偶发 “Unable to allocate shared memory segment” 错误。我的处理步骤是这样的第一步先确认当前状态df -h /dev/shm free -h mount | grep /dev/shm发现 /dev/shm 已用 14.2G非常接近 16G 的上限说明空间确实紧张。再看 free内存总量 32Gused 20Gavailable 10G系统内存还够说明有空间可以腾给 tmpfs。第二步决定把 /dev/shm 调整到 24G。因为 Oracle 的 SGA 配置是 12G加上 Java 进程和其他服务的共享内存需求24G 是一个相对稳妥的数值且不会让系统可用内存低于安全线。第三步先临时验证sudo mount -o remount,size24G /dev/shm df -h /dev/shmmount | grep /dev/shm确认一次性扩展到 24G 没有问题后再写入 /etc/fstab并执行 mount -a 验证配置没写错。第四步重启 Oracle 实例和 Java 服务。观察半小时日志不再报共享内存分配失败问题解决。事后复盘时发现其实即使不调整 /dev/shm也可以通过修改 Oracle 的 memory_target 参数来降低共享内存需求但那样会影响数据库性能。调整 /dev/shm 是更直接的方案。8. 实操中积累的几个真实心得关于 /dev/shm 的调整我在实际操作中积累了一些心得。第一调整 /dev/shm 大小的本质是“调整内存使用策略”而不是单纯地“加一个挂载参数”。在调大之前一定要先评估系统的物理内存和业务实际需求避免把内存让给了 tmpfs 反而导致其他进程内存不足。第二能用临时 remount 就先不要直接改 fstab。临时调整方式的风险最小即使写错了也不会影响重启。但也要及时同步到 fstab 里否则重启后回滚到默认值业务可能又会遇到同样的问题。第三对于数据库和容器场景不要依赖默认值。默认值“物理内存的一半”是通用策略不会为特殊业务定制。跑 Oracle、跑大数据组件、跑浏览器自动化都需要提前评估共享内存需求主动做出调整。第四写入测试文件后记得现场清理。用一个 1GB 的测试文件验证空间没问题后一定要 rm 掉否则这个小文件会一直占着内存不释放积少成多还有可能挤占其他业务的内存空间。最后再分享一个小技巧如果你经常需要快速查看 /dev/shm 的使用情况可以写一个简单的 shell 别名alias shmstatdf -h /dev/shm echo --- lsof L1 | head -20这样每次执行 shmstat就能同时看到容量使用情况和占用共享内存文件的进程列表排查问题的时候效率会高不少。这个小工具在我日常运维中帮了我很多次算是比较值得养成的习惯。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑