/dev/shm 写满导致 nginx shared memory 分配失败,因其依赖 shm_open() 在 tmpfs 类型的 /dev/shm 中创建内存段,空间耗尽时返回 ENOSPC;需通过 fstab 持久化扩容或优化 nginx zone 配置来解决。

为什么 /dev/shm 写满会让 nginx 的 shared memory 分配失败
nginx 的 limit_req_zone、limit_conn_zone、ssl_session_cache 等功能依赖 shm(POSIX shared memory),底层通过 shm_open() 在 /dev/shm 下创建匿名内存段。这个目录本质是 tmpfs,挂载在内存中,默认大小通常为 64MB(取决于内核版本和发行版)。一旦其他进程(如数据库临时表、Python 的 multiprocessing、自定义 shm 段)持续写入且未清理,/dev/shm 耗尽,nginx reload 或启动时调用 shm_open() 就会返回 ENOSPC,日志里出现类似:
nginx: [emerg] unable to create shared memory zone "xxx": cannot open shared memory object "/xxx" (28: No space left on device)
这不是磁盘空间问题,而是 tmpfs 内存配额用完了。
调整 /dev/shm 大小的两种可靠方式
不能只靠 mount -o remount,size=... 临时生效,必须确保重启后持久、且不被 systemd-tmpfiles 覆盖。
- 推荐方案:修改
/etc/fstab,添加或修正这一行(例如设为 1G):tmpfs /dev/shm tmpfs defaults,size=1G 0 0 - 替代方案(仅限 systemd 系统):创建
/etc/tmpfiles.d/shm.conf,内容为:u root root 1777 /dev/shm 0 0
然后执行systemd-tmpfiles --create;但注意:该方式不支持直接指定 size,需配合MountFlags=shared和额外 mount unit,不如 fstab 稳定 - 验证是否生效:
findmnt -t tmpfs /dev/shm应显示正确 size;df -h /dev/shm需反映新值
nginx 自身 shm 使用量是否可优化
增大 /dev/shm 是兜底手段,但更应检查 nginx 配置是否过度申请共享内存——尤其当 zone 名称重复、reload 频繁、或使用了未清理的第三方模块时。
-
limit_req_zone和limit_conn_zone的zone=name:size中size值要合理:1MB 通常可容纳约 16k key(取决于 key 长度和 hash 表负载因子),不必默认写 100M - 避免在
http块外(如stream或多个server里)重复定义同名 zone,否则每次 reload 都新建 shm 段,旧段残留(直到所有 worker 退出) - 检查是否有模块(如
ngx_http_geoip2_module或自研模块)在 init 阶段调用shm_alloc但未正确注册 cleanup 回调 - 临时诊断:
ls -l /dev/shm/ | grep nginx可看到残留的/nginx_*段;正常运行时一般只有 1–2 个,若数量持续增长,说明有泄漏
监控和自动清理的实用建议
生产环境不能只靠人工发现 /dev/shm 满了才处理,得有防御机制。
- 加入基础监控项:
df -P /dev/shm | awk 'NR==2 {print $5}' | sed 's/%$//',告警阈值建议设为 80% - 不建议 cron 定期
rm -f /dev/shm/nginx_*:可能误删正在使用的段,导致 nginx worker crash(SIGBUS) - 安全清理方式:先确认哪些 shm 段无关联进程:
lsof +D /dev/shm 2>/dev/null | grep -v "nginx:";再针对无输出的文件执行rm - 更稳妥的做法是限制上游使用方:比如 PostgreSQL 的
temp_buffers、Python 的multiprocessing.shared_memory显式指定 name 并 ensure close/unlink,避免无名段堆积
真正麻烦的不是容量不够,而是不同组件对 /dev/shm 的生命周期管理策略不一致——nginx 依赖进程退出自动释放,而有些应用会显式 unlink 却忘了 close,有些则完全不 unlink。这类混合使用场景下,光调大 size 只是延缓问题爆发时间。










