许多开发者在处理 Linux 核心转储(core dump)时,常遇到一个令人困惑的问题:明明在终端执行了ulimit -c unlimited,程序崩溃后却怎么也找不到 core 文件。这背后隐藏着多个容易被忽略的机制与细节,下面逐一深入剖析。
ulimit -c unlimited 为何只在当前终端生效
首先要澄清一个关键认知误区。ulimit -c本质上是 per-process 级别的软限制,仅影响当前 shell 会话以及从该 shell 直接 fork 出的子进程。换句话说,你在一个终端执行的命令,对另一个终端中的程序、系统服务或后台任务完全不起作用。
以下是几个典型的翻车场景:
- 在桌面终端设置了
ulimit -c unlimited,然后双击启动 GUI 程序——该程序并不会继承这个 shell 的配置。 - 将脚本放入
/etc/cron.d/中,崩溃后无 core 生成——因为 cron daemon 拥有独立的 ulimit 设置。 - 使用
systemctl start myapp.service启动服务,同样没有 core——systemd 的资源限制是隔离的,必须在 unit 文件中显式声明LimitCORE=infinity。
最直接的验证方法:在你要运行程序的目标环境(如终端窗口、服务上下文)中,直接执行ulimit -c,输出必须为unlimited或一个具体数值,否则配置未生效。
/proc/sys/kernel/core_pattern 路径写入失败的三个硬性条件
内核在写入 core 文件前,会依次检查三道关卡,缺一不可。
第一,目标路径的父目录必须已存在,且权限为1777(sticky bit 加所有人可读写执行)。例如,若设置/var/crash/core.%e.%p.%t,需提前用sudo mkdir -p /var/crash && sudo chmod 1777 /var/crash创建。注意,755或775均不满足要求。
第二,如果core_pattern以|开头(如|/usr/lib/systemd/systemd-coredump %P %u),表示走用户态处理器。此时必须确保对应二进制文件可执行、有权限,且服务已启用——systemctl is-active systemd-coredump应返回active。
第三,fs.suid_dumpable参数默认值为0,这意味着使用sudo、passwd等 setuid 程序运行时,即使 ulimit 允许也会拒绝生成 core。必须显式改为2:echo 2 | sudo tee /proc/sys/fs/suid_dumpable。这个细节最容易被忽视,很多 setuid 程序崩溃后无 core,最终排查发现就是这里卡住了。
systemd 服务如何开启 core dump
如果程序由 systemd 管理,前面提到的修改 ulimit 或/etc/security/limits.conf均无效。systemd 的资源限制体系完全独立于用户 shell。
正确做法是编辑服务的覆盖配置:
- 执行
sudo systemctl edit myapp.service - 填入:
[Service]LimitCORE=infinity
- 如果程序使用了大页内存或 memlock,可能还需额外添加
MemoryLimit=infinity和LockPages=false - 最后重载配置并重启:
sudo systemctl daemon-reload && sudo systemctl restart myapp.service
但需注意,某些发行版(如 RHEL 8 及以上)默认禁用了LimitCORE,即使写入infinity也不一定生效。此时需用systemctl show myapp.service | grep LimitCORE确认实际值是否匹配。
core 文件生成了但内容为空或截断?
文件已生成但内容异常,这通常不是配置遗漏,而是管道传输或内存标记导致的静默数据丢失。
一种情况是当core_pattern指向管道(如|/usr/lib/systemd/systemd-coredump)时,/proc/sys/kernel/core_pipe_limit控制着缓冲区大小。默认值为0(不限),但若被人为改为小值(如1),多进程同时崩溃时,后续进程会被丢弃。
另一种情况是某些程序(尤其是开启了 memlock 或使用大页内存的)可能因 VMA 标记(如VM_DONTDUMP)跳过了部分内存段。这并非配置问题,而是程序自身要求。排查方法:用readelf -l core | grep LOAD查看实际加载的 segment 数量,再与原二进制文件的readelf -l /path/to/binary对比,即可快速判断是否缺段。
最后再次强调最容易被忽略的细节:setuid 程序必须配置fs.suid_dumpable=2,且该值不会持久保存。即使你在/etc/sysctl.conf中写入了,也需记得执行sysctl -p才能真正生效。
