进程收到致命信号(如 SIGSEGV、SIGABRT)时,内核可写出内存快照(core)。配合 GDB 可回溯调用栈、查看变量,是定位段错误、空指针、断言失败的有效手段之一。
本文按 先查现状 → 再开启 → 配路径 → 分析 → 排坑 的顺序说明。
一、看当前coredump是否开启以及存储路径
ulimit -c # 看当前这个 shell 里,进程允许生成的 core 文件大小上限ulimit -a # 列出当前 shell 下各种资源的 soft 限制(文件数、栈大小、core 大小等)sysctl kernel.core_pattern #读内核参数,看core写到哪里、叫什么名字,或是否交给别的程序处理cat /proc/sys/kernel/core_pattern # 看coredump路径# 若输出以 | 开头,说明由 systemd-coredump 等用户态程序接管systemctl status systemd-coredump 2>/dev/nullcoredumpctl list #列出本机由 systemd-coredump 保存的历史 coredump 记录
ulimit -c | |
|---|
0
| 不生成 core |
unlimited
| 不限制 core 大小 |
正整数 | core 大小上限(单位依 shell/实现,常见为 1024 字节块,勿简单等同「字节」) |
二、如何开启 Coredump
2.1 临时开启(仅当前 shell)
关闭终端或退出 SSH 后失效。systemd 管理的服务不会继承当前 shell 的 ulimit。
2.2 永久开启:limits.conf
编辑 /etc/security/limits.conf
(语法:domain type item value,不是 shell 脚本,不能 source):
* soft core unlimited* hard core unlimited
仅对特定用户生效时,将 * 换成用户名(如 user1234)。
| | |
|---|
文件性质 | 当成 shell 脚本,source 一下 | 键值对,由 PAM(pam_limits.so)解析 |
生效时机 | 改完当前终端立刻生效 | 需重新登录或 restart 目标服务 |
已运行进程 | 改配置后自动更新 | 不会变,必须新会话 / 新进程 |
与 ulimit | 是一回事 | ulimit 改当前 shell;limits.conf 改登录时的默认上限
|
修改后如何生效:
| |
|---|
SSH 会话 | 断开重连 |
守护进程(nginx、自定义服务等) | systemctl restart <服务>
|
已在运行的旧进程 | 必须重启进程或重新登录 |
2.3 systemd 服务(更稳妥)
在 unit 中配置:
[Service]LimitCORE=infinitysystemctl daemon-reloadsystemctl restart <服务名>
查看是否生效:
systemctl show <服务名> -p LimitCORE
2.4 登录 Shell 兜底:/etc/profile(和2.2效果差不多,本人最喜欢,推荐)
在 /etc/profile 末尾追加
需要新Shell终端生效。
2.5 代码里开启
/* 错误:ulimit 只作用于 system() 起的子 shell */system("ulimit -c unlimited");在 main() 最早处调用:#include<sys/resource.h>voidenable_core_dump(void){struct rlimit rl;rl.rlim_cur = RLIM_INFINITY;rl.rlim_max = RLIM_INFINITY;setrlimit(RLIMIT_CORE, &rl);}
进程的 soft limit 不能超过父进程或 systemd 设置的 hard limit。
2.6 开启core总览表
| | | | |
|---|
当前终端 ulimit -c
| 当前 shell | 仅本 shell + 其子进程 | 立即 | 否 |
/etc/profile
| 登录 shell 启动时 | 交互式登录 shell 及其子进程 | 下次登录 shell 启动时;当前终端、同次 SSH 新标签不自动变 | |
limits.conf
| PAM(pam_limits) | 建立登录会话时的进程(SSH、su - 等) | 重新登录 / su - 后;不改已跑进程 | |
systemd LimitCORE
| systemd 起服务时 | 该 unit 管理的服务及其子进程 | daemon-reload + restart 服务后
| |
代码中设置 | 进程自己在 main 最早处 | 本进程 + 其子进程(受父级 hard 限制) | 进程启动后立刻(调用那一刻) | 否,每次启动都要执行;且不能超过父进程/systemd 的 hard limit |
三、配置 Core 文件存放路径
由 kernel.core_pattern 决定写到哪里、叫什么名字。
3.1 永久配置
新建 /etc/sysctl.d/99-core.conf:
kernel.core_pattern = /var/core/core-%e-%p-%tsudo mkdir -p /var/coresudo chown <运行用户>:<组> /var/coresudo sysctl -p /etc/sysctl.d/99-core.conf
3.2 占位符速查
| |
|---|
%e
| 可执行文件名(不含路径,最长约 15 字符) |
%p
| 进程 PID |
%t
| 崩溃时间戳(epoch 秒) |
%s
| 信号编号 |
%h
| 主机名 |
%E
| 可执行文件完整路径(/ 替换为 !) |
%c
| 崩溃进程的 core 软限制大小 |
若 core_pattern 为相对路径(如 core-%e-%p-%t),core 会写在进程当前工作目录;systemd 服务需对齐 WorkingDirectory。
3.3 临时改路径(重启后丢失,除非写入 sysctl 配置)
sudo sysctl -w kernel.core_pattern='/tmp/core-%e-%p'
四、查Coredump 存储路径
sysctl kernel.core_pattern# 或cat /proc/sys/kernel/core_pattern
4.1 传统落盘
示例 pattern:
文件出现在 /var/core/,名称类似 core-myapp-12345-1716633600。
4.2 systemd 接管
systemd 接管是指,进程崩溃时,内核不直接把 core 写成你磁盘上的某个文件,而是把 core 数据交给 systemd 自带的程序 systemd-coredump 去处理、保存,不一定出现在你配置的 /var/core/ 路径。
示例 pattern(以 | 开头):
|/usr/lib/systemd/systemd-coredump %P %u %g %s %t %c %h
磁盘上不一定有「一个完整路径的 core 文件」,使用:
coredumpctl listcoredumpctl info <PID>coredumpctl gdb <可执行文件> <PID>
五、用 GDB 分析 Core
gdb /path/to/binary /path/to/core-file
GDB 内常用命令:
bt # 调用栈bt full # 带局部变量info threads # 多线程frame 3print some_var
二进制需与崩溃时一致,且最好带 -g 调试符号;否则栈中可能只有地址,难以对应源码行号。
查看某进程是否允许产生 core:
cat /proc/<pid>/limits | grep -i "Max core file size"
六、没有看到 Core 时排查
| | |
|---|
完全没有 core | ulimit -c 为 0
| ulimit -c unlimited 或 LimitCORE=infinity
|
完全没有 core | 被 systemd-coredump 接管 | coredumpctl list
|
有信号、无文件 | core 目录不可写 | 检查 owner/权限 |
有信号、无文件 | 磁盘满 | df -h
|
GDB 只有地址 | 无调试符号 | Debug 构建或保留未 strip 二进制 |
SaveCoreDump 失败 | core 不在 CWD | 对齐 core_pattern 与 WorkingDirectory |
七、禁用core
不需要 core 时:
ulimit -c 0或在 limits.conf / systemd 中设置 LimitCORE=0。
八、部署检查清单
sysctl kernel.core_patternsystemctl show <可执行文件名> -p LimitCOREsystemctl status coredump-cleanup.timerulimit -c # 在运行服务的用户会话下执行
异常链路中 handler_node 已注册 coredump → SaveCoreDump 动作;崩溃后除手动 GDB,也可在异常上报流程里自动归档 core。
结语
开启 Coredump 并不复杂,容易踩坑的是三层配置要对齐:
- ulimit / LimitCORE — 能不能写
把这三层对齐,再配合 -g 编译和 GDB,就可以通过coredump来看bug现场。