简介:《转储配置.pdf》是一份面向SAP物料管理(MM)模块顾问、实施人员及系统维护者的SAP转储配置实操详解,聚焦采购订单与库存运输订单的常见配置痛点。压缩包内为1个PDF文档,大小3.36MB,内容结构清晰,适合本地查阅。该资源已有171人学习,阅读价值获得初步验证。文档从事务代码OMH6设置的采购订单编号范围入手,逐步展开采购订单类型配置(事务代码OMEU)、跳号问题解决(通过SNRO关闭缓冲)、报表查询按类型分类、定价方案差异化配置,以及通过T160M/V_160M设置错误提示与价格差异限制等关键环节;同时涉及采购订单审批机制、价格与物料法定价格差异的提示规则、后台配置点OMF4等,并配有使用SE16直接修改视图的实际操作经验。对于需要在SAP系统中搭建或优化转储配置,尤其是处理库存运输订单类型划分、编号跳号、采购价格管控等难题的读者,可直接参考其中的配置路径与操作技巧。
1. 转储配置:系统崩溃时唯一能留住现场的手段
看到《转储配置.pdf》这个名字,你大概已经猜到,它讲的是 Linux 核心转储(coredump)的配置方法。这份文档解决的场景很窄但很痛:某天凌晨业务进程段错误退出,日志里只有一句 signal 11,你连崩溃时的调用栈都拿不到,只能靠猜。转储配置就是为这种时刻准备的——它让内核在进程崩溃瞬间把内存快照写下来,之后用 gdb 打开,崩溃那一帧的代码、参数、调用链全都在里面。适合所有被线上诡异崩溃折磨过的 Linux 运维、SRE 和后端开发者。如果你系统里出现过“未配置任何 coredump 目标。无法保存主机核心转储”这类提示,这篇文章正好能帮你把缺口补上。
2. 先看懂转储链路:内核信号、core_pattern 与 systemd-coredump 的三方协作
2.1 崩溃信号与内存映像:转储能存下什么
进程不是平白无故消失的。以最常见的段错误为例:x86_64 上访问非法地址触发 CPU 异常,内核在异常处理路径里向进程发送 SIGSEGV,默认动作是“终止并清理”。但在真正清理之前,内核会先检查这个进程是否允许产生 core dump,允许就执行 do_coredump 流程,把内存映像写出去。
这里有三道闸门。第一道是信号类型:能触发转储的通常是有 core 动作的信号,包括 SIGSEGV、SIGABRT、SIGBUS、SIGILL、SIGFPE、SIGTRAP 这些;但 SIGKILL 和 SIGSTOP 不会产生 core,因为这两个信号默认动作里根本没有“写转储”这个环节。第二道是资源限制:进程的 RLIMIT_CORE 为 0 时,无论信号多标准,内核直接放弃转储。第三道是 dumpable 标志:进程通过 setuid 降权、或者调用 prctl(PR_SET_DUMPABLE, 0) 之后,内核会认为这个进程的内存不值得信任,拒绝把它写入磁盘。三道闸门全过,core 文件才会落地。
那 core 文件里到底有什么?进程地址空间中可读写的匿名内存、共享内存映射、寄存器上下文、程序头信息,以及当时打开的文件描述符和命令行参数。简单说,就是进程在崩溃瞬间的完整“内存快照”。栈上残留的局部变量、堆上还没被清零的对象、调用链每一层的返回地址,全被原样保留。gdb 拿到它,能还原出崩溃时刻的程序计数器位置和完整调用栈,这就是转储配置的价值所在。
有时候你会发现同一个程序,有时候崩出 core,有时候崩不出,差别往往不在信号,而在 coredump_filter。这个文件控制哪些内存段会被写进转储,默认值已经覆盖匿名私有内存、匿名共享内存和 ELF 文件头映射。Java 这类动不动就映射大块内存的程序,转储文件经常有几 GB,就是因为它把整个堆都算进了匿名内存。理解这一点,你才能解释为什么同一个配置,C++ 服务转储只有几百 MB,Java 服务却能写满磁盘。
2.2 core_pattern 与内核参数:转储目标由谁决定
真正决定“内存快照往哪送”的,是内核参数 kernel.core_pattern,它写在 /proc/sys/kernel/core_pattern 里,改一次立即全局生效。传统写法是给一个路径模板,比如:
/var/crash/core.%t.%p.%e这里的 %t、%p、%e 叫模板符,内核在落盘时按实际情况替换。常用模板符如下:
| 模板符 | 含义 | 示例值 |
|---|---|---|
| %p | 崩溃进程 PID | 3312 |
| %u | 崩溃进程 UID | 1000 |
| %g | 崩溃进程 GID | 1000 |
| %t | 崩溃时刻的 epoch 秒 | 1720000000 |
| %e | 可执行文件名(不含路径) | nginx |
| %h | 主机名 | web-01 |
| %s | 触发崩溃的信号编号 | 11 |
| %c | 当前 core 文件大小限制 | unlimited |
生产环境我习惯用 %t.%p.%e 三段组合。%t 保证不同时间崩溃的文件不重名,%p 防止同一秒内多个进程崩溃时互相覆盖,%e 让你一眼看出是哪个程序出了问题。只写 %e 的话,两个同名进程先后崩溃会把前一个文件盖掉,这种教训在真实环境里不少见。
如果 core_pattern 以 | 字符开头,内核就不再写文件,而是把转储内容通过管道交给后面的程序,由那个程序决定怎么存。现代发行版默认正是这么干的:core_pattern 被指向 systemd-coredump,内核职责止步于“把内存快照吐给管道”,后续的存储、压缩、元数据记录全交给用户态的 systemd 组件。
顺便说一句,跟 core_pattern 配套的还有 core_uses_pid 和 core_pipe_limit。前者是旧时代的产物,在没有模板符的年代把 PID 直接拼在文件名末尾,现在用不用都行;后者控制并发转储时的管道排队策略,多进程同时崩溃时如果发现转储偶发丢失,值得查一下这个值是不是被改成了很小的数。大多数情况下,你只需要盯住 core_pattern 一个参数就够了。
2.3 systemd-coredump:默认托管者与两代方案对比
RHEL 8/9、Ubuntu 18.04 之后的发行版,默认把 core_pattern 指向 systemd-coredump,形如:
|/usr/lib/systemd/systemd-coredump %P %u %g %s %t %h %esystemd-coredump 做的事有三件:把 core 写入 /var/lib/systemd/coredump/,把崩溃进程的元数据(信号、PID、命令行、环境变量摘要)写进 journal,然后对 core 文件做压缩。用户用 coredumpctl list 就能看到所有转储记录,不用自己翻目录拼路径,这是它比传统路径模板强的地方。
两代方案的主要差别:
| 对比项 | 传统路径模板 | systemd-coredump |
|---|---|---|
| 配置入口 | kernel.core_pattern | /etc/systemd/coredump.conf |
| 文件命名 | 自定模板符,直观 | 按 systemd 规则生成 |
| 崩溃元数据 | 完全没有 | 写入 journal,coredumpctl 可查 |
| 文件压缩 | 不压缩 | 默认压缩,省空间 |
| gdb 前处理 | 直接打开 | 需要先解压或确认格式 |
| 排查入口 | 自己 ls 目录 | coredumpctl list 统一查 |
选型没有绝对对错,我自己的习惯是:测试环境用传统路径模板,崩溃后 ls 一眼就能看到文件,调试路径短;生产服务器走 systemd-coredump 托管,因为它的元数据记录对事后追查太有用了——你不仅知道崩了,还知道崩之前命令行是什么、环境变量是什么。但它也有个让人不舒服的地方:文件是压缩的,直接 gdb 会报格式不识别,这个坑第四章会专门讲。
3. 动手配置一套可落地的转储方案:参数、命令与验证起点
这一章给的是可以直接抄的作业。从检查现状到写配置,每一步的命令和参数都摆在下面,照做就能把转储配置跑通。完整验证放第五章,但本章末尾会给你一个最小确认手段,确保配置没白写。
3.1 动手前先查三处现状,别上来就改
# 1) 当前转储目标模板,确认是文件路径还是管道 cat /proc/sys/kernel/core_pattern # 2) 当前 shell 下 core 文件大小限制,0 表示禁止转储 ulimit -c # 3) systemd-coredump 是否在运行 systemctl status systemd-coredump.socket --no-pager | head -10 # 4) 转储目录所在分区的剩余空间 df -h /var/crash /var/lib/systemd/coredump 2>/dev/null这四个命令的输出决定后续动作。如果 core_pattern 已经被清空,或者指向了一个不存在的管道程序,系统诊断时就会弹出“未配置任何 coredump 目标。无法保存主机核心转储”。这行提示看着吓人,但它只说明一件事:内核不知道把转储交到谁手里。 如果 ulimit -c 显示 0,别急着怀疑内核,先把限制解开再试。注意 ulimit 是 shell 内建命令,只对当前 shell 及其子进程生效,通过 systemd 启动的服务根本不看它,服务进程看的是 /proc/ /limits 里的 Max core file size。
3.2 传统文件路径方案:用 sysctl 写模板
假设我们决定走传统方案,目标目录定为 /var/crash:
# 临时修改,立即生效,重启后丢失(先验证配置本身) sysctl -w kernel.core_pattern=/var/crash/core.%t.%p.%e sysctl -w kernel.core_uses_pid=1 # 持久化:写入 /etc/sysctl.d/ 下独立文件 echo 'kernel.core_pattern=/var/crash/core.%t.%p.%e' > /etc/sysctl.d/99-coredump.conf echo 'kernel.core_uses_pid=1' >> /etc/sysctl.d/99-coredump.conf # 重载全部 sysctl 配置并确认结果 sysctl --system cat /proc/sys/kernel/core_pattern这段命令的逻辑是先用 -w 在运行态立即改,验证配置本身有没有问题,确认无误后再把同样内容写进 /etc/sysctl.d/ 下的独立文件。独立文件的优势是升级系统时不会跟发行版自带的 /etc/sysctl.conf 冲突,后缀 99 保证它的优先级排在默认配置之后。core_uses_pid 设成 1 是双保险,模板符里已经有 %p,二者不冲突,但有些老脚本只看这个参数,设上能让它们少报一个错。
提示:修改 /etc/sysctl.d/ 下的文件不会自动生效,必须执行
sysctl --system重载,否则重启前配置还是旧的。
目录权限这一步经常被忽略。/var/crash 如果不存在,内核创建文件会失败;权限不对,非 root 进程崩溃时也没法写。我一般这样处理:
mkdir -p /var/crash chmod 1777 /var/crash用 1777 而不是 755,是为了让任何用户启动的进程都能把 core 写进来,同时权限位里的 sticky bit 能防止普通用户互相删文件。如果你担心安全问题,可以改成 1733 并把目录属主设成专用账号,但绝大多数内部服务器用 1777 就够了。
3.3 systemd-coredump 方案:只动覆盖文件,别改主配置
如果发行版默认就走 systemd-coredump,我的建议是不要去改写 core_pattern 指向自己的脚本,而是在 /etc/systemd/coredump.conf.d/ 下加一个覆盖文件:
mkdir -p /etc/systemd/coredump.conf.d cat > /etc/systemd/coredump.conf.d/99-local.conf <<'EOF' [Coredump] Storage=external Compress=yes ProcessSizeMax=2G ExternalSizeMax=2G EOF systemctl daemon-reload systemctl restart systemd-coredump.socket这里四个参数是核心。Storage=external 表示 core 文件写入 /var/lib/systemd/coredump/,journal 只留元数据;如果设成 journal,二进制 core 会被直接塞进日志,一个 1G 的转储就能把默认大小的 journal 撑爆。Compress=yes 是默认行为,转储文件压缩后通常只有原来的三分之一到五分之一,代价是 gdb 前要处理压缩格式。ProcessSizeMax 限制参与转储的进程最大内存映像,超过则不转储;ExternalSizeMax 限制压缩后输出文件的最大尺寸,防止超大进程把磁盘写满。生产上这两个值参考你的业务内存设定,Java 服务几百 GB 堆的话,2G 显然不够,要按实际情况调。
改完配置后,用下面的命令确认覆盖文件真的被读到了:
systemctl show systemd-coredump.socket --property=Listen systemd-analyze cat-config systemd/coredump.conf第一条确认 socket 正常监听,第二条把生效配置完整打印出来,如果 99-local.conf 的内容没出现在输出里,说明文件放错位置或者语法有问题。
3.4 服务进程的转储限制:LimitCORE 与参数速查
传统方案和 systemd 方案解决的都只是“内核把转储写到哪”,但服务进程自己设的限制能直接掐断整条链路。通过 systemd 启动的 nginx、java、PostgreSQL,shell 里敲 ulimit 根本影响不到它们,必须在 unit 文件的 [Service] 段里写:
[Service] LimitCORE=infinity Environment=ULIMIT_CORE=unlimitedLimitCORE=infinity 把进程的硬限制和软限制都放开,服务崩溃时内核才允许转储。改完记得 systemctl daemon-reload 再重启服务,否则不生效。这条是服务类进程转储“完全不生效”的头号原因,比 core_pattern 写错还要常见,因为很多人只查了内核参数,没查进程自身的限制。
常用转储配置参数速查:
| 参数 | 所在位置 | 作用 | 常用值 |
|---|---|---|---|
| kernel.core_pattern | /proc/sys/kernel/core_pattern | 转储目标路径或管道 | /var/crash/core.%t.%p.%e |
| kernel.core_uses_pid | /proc/sys/kernel/core_uses_pid | 文件名附加 PID | 1 |
| Storage | coredump.conf [Coredump] | 转储存储模式 | external |
| Compress | coredump.conf [Coredump] | 是否压缩转储 | yes |
| ProcessSizeMax | coredump.conf [Coredump] | 参与转储的进程内存上限 | 2G |
| ExternalSizeMax | coredump.conf [Coredump] | 转储文件输出上限 | 2G |
| LimitCORE | systemd unit [Service] | 服务进程 core 限制 | infinity |
配完这一套,跑一次最小验证:新开一个 shell,执行 ulimit -c unlimited,然后运行 python3 -c 'import ctypes; ctypes.string_at(0)',程序会立即段错误退出。接着看你的方案对应目录里有没有新增 core 文件,有就说明链路通了;没有就继续看下一章,问题多半在那几个坑里。
4. 转储配置避坑:5 个反复出现的“不生效”现场
这一章全部来自血泪经验。很多所谓转储配置的玄学问题,拆开看都能归到下面五类,按现象对号入座就行。
4.1 系统提示“未配置任何 coredump 目标。无法保存主机核心转储”
现场:跑诊断脚本或系统巡检工具时,输出里直接出现这句,同时 /proc/sys/kernel/core_pattern 指向的管道程序根本不存在,或者被某个初始化脚本写成了空。
原因:core_pattern 里的管道目标路径写死了,但机器之间发行版不同,systemd-coredump 的位置并不统一。有人在 RHEL 上把路径写死为 /usr/lib/systemd/systemd-coredump,换到别的发行版就成了死路径;更常见的是某个初始化脚本执行 sysctl -w kernel.core_pattern="" 想“关闭核心转储”,结果留下一个无法判定的空目标,诊断工具只能报这个提示。
处理:先用 command -v 确认 systemd-coredump 的真实路径,再把它写回 core_pattern:
CORE_PIPE="$(command -v systemd-coredump)" sysctl -w kernel.core_pattern="|$CORE_PIPE %P %u %g %s %t %h %e" echo "kernel.core_pattern=|$CORE_PIPE %P %u %g %s %t %h %e" > /etc/sysctl.d/99-coredump.conf sysctl --system如果 command 没有输出,说明系统根本没装 systemd-coredump,那就退回传统路径模板方案,把 3.2 的配置写进去。反过来,如果你确实想关闭核心转储,正确做法是写成指向 /bin/true 的管道,或者直接设置 Storage=none,别把参数清空。
4.2 core 文件完全没生成,日志里连痕迹都没有
现场:用会段错误的小程序测试,退出码是 139(128+11),但 /var/crash 和目标目录空空如也,journal 里也没有任何 coredump 记录。
原因:进程的 RLIMIT_CORE 是 0。shell 直接运行的进程,ulimit -c 默认在很多发行版就是 0;systemd 启动的服务则要看 /proc/ /limits 里的 Max core file size。最容易误判的点在于:你已经在 ssh 会话里敲了 ulimit -c unlimited,但那只是当前 shell 和它的子进程,其他会话和系统服务根本不继承这个设置。
处理:先看真实运行进程的限制:
grep "Max core file size" /proc/<实际pid>/limits对服务进程,在 unit 文件 [Service] 段补 LimitCORE=infinity,daemon-reload 后重启服务。对临时测试,必须在同一个 shell 里先执行 ulimit -c unlimited,再启动被测进程,两个动作不能在两个会话里分开做,这是新手最容易踩的坑。
4.3 core 文件生成在进程工作目录,预期目录里没有
现场:配置写的是 kernel.core_pattern=core.%p,崩溃后文件出现在进程的工作目录,systemd 服务的工作目录通常跟着 ExecStart 路径走,你翻遍了 /var/crash 也找不到。
原因:core_pattern 里写的是相对路径。内核遇到相对路径时,以崩溃进程的 cwd 为基准创建文件,而服务进程的 cwd 往往不是你以为的 /var/crash。3.2 里强调写绝对路径,就是为了绕开这个坑——绝对路径下任何进程崩溃都往一个固定位置写,不依赖 cwd。
处理:把 core_pattern 改成绝对路径,同时检查目标目录的 SELinux 上下文。很多 CentOS/RHEL 机器只改了路径没管 SELinux,结果是 core 文件创建失败,journal 里出现 avc denied 记录。用下面两个命令排查:
cat /proc/sys/kernel/core_pattern grep "avc.*denied" /var/log/audit/audit.log | tail -20如果是 SELinux 拦截,执行 restorecon -Rv /var/crash 恢复目录上下文,或者用 semanage 给转储目录打上正确的类型标签。
4.4 coredumpctl list 有记录,但转储文件不在预期目录
现场:配好 systemd-coredump 后用 coredumpctl list 能看到崩溃记录,但 /var/lib/systemd/coredump 下找不到任何文件,journal 分区占用却涨得飞快。
原因:coredump.conf 的 Storage 被设成了 journal。这个模式下 systemd-coredump 把整个 core 当作 journal 字段写进日志,不生成独立文件。journal 里的二进制数据没法直接交给 gdb,而且 journal 空间有上限,一个 2G 的转储就能把默认配置的 journal 挤爆,顺带影响系统其他日志的正常写入。
处理:改成 external 或 both:
cat > /etc/systemd/coredump.conf.d/99-storage.conf <<'EOF' [Coredump] Storage=external EOF systemctl daemon-reload systemctl restart systemd-coredump.socket改完后再触发一次崩溃,用 coredumpctl info 查看新记录的存储模式,确认已变成 external。旧记录不受影响,但也不值得去抢救,重新触发一次更干净。
4.5 gdb 打不开 core:文件存在,但报 file format not recognized
现场:core 文件名称对、大小也合理,但 gdb 打开时报“不是 core 文件”,用 file 命令一看,是一份 zstd 或 xz 压缩数据。
原因:systemd-coredump 默认对转储做压缩,而传统路径模板方案不压缩。很多人拿到 core 文件习惯性直接 gdb,没注意文件后缀和 magic number,自然打不开。
处理:先用 file 判断真实格式,再解压后分析:
file /var/lib/systemd/coredump/core.xxx zstd -d /var/lib/systemd/coredump/core.xxx.zst -o /tmp/core.xxx gdb /path/to/binary /tmp/core.xxx如果 team 里所有同事都习惯直接 gdb,可以把 Compress 设为 no 省去每回解压,代价是磁盘占用上升。我个人保留压缩,把解压动作写进调试脚本,因为磁盘被几个 5G 的未压缩 core 塞满时你会后悔当初的偷懒。
5. 转储配置完成后怎么验证:从模拟崩溃到自动化巡检
配置写完了,不验证等于没配。一套转储配置没经过真实崩溃检验,永远不知道会在哪个环节断掉。这一章从手动触发到定时巡检,给出能直接用的命令和脚本。
5.1 用一段会崩溃的小代码模拟真实场景
不推荐用 kill -s SIGSEGV $$,因为 shell 对自身信号的处理可能绕过内核默认动作,测试结果不可信。用 Python 一行就能触发标准段错误:
# 触发一次空指针解引用,进程退出码应为 139(128+SIGSEGV) python3 -c 'import ctypes; ctypes.string_at(0)'验证按顺序执行四步:
- 在当前 shell 执行 ulimit -c unlimited;
- 运行上面的 Python 命令,记住退出码必须是 139 才说明是 SIGSEGV 崩溃;
- 传统方案看 ls -l /var/crash/ 是否新增 core 文件,systemd 方案执行 coredumpctl list 看是否有新记录;
- 用 file 命令确认生成文件的类型是 ELF core,如果显示 compressed data,回到 4.5 处理。
如果第 3 步就断了,回到第四章按现象排查;如果第 4 步异常,多半是压缩配置和调试工具链不匹配。这一步能排掉 90% 的“配置文件改了但等于没改”的情况。
5.2 二次验证:确认服务进程的转储链路是通的
有些场景下系统级配置是通的,但业务进程因为是 systemd 服务启动,有自己的 rlimit 设置,比如 Java 服务。针对这种情况,验证的重点是 /proc/ /limits 里的实际值,而不是 shell 里的 ulimit:
pid=$(systemctl show -p MainPID --value <服务名>) grep "Max core file size" /proc/$pid/limits输出应该是 unlimited。如果显示 0,说明 unit 文件里的 LimitCORE 没生效或没重启,回到 3.4 处理。这一步做完了,还可以用 gdb attach 到进程发一个 SIGSEGV 来触发转储——但千万别在生产环境这么干,测试环境和预发环境做一次就够了。验证结束后记得把临时加的 LimitCORE 和 ulimit 恢复原状,别为了查一个问题把所有进程的 core 都开放了。
5.3 自动化巡检:一条脚本定时摸底
转储配置最容易出现的问题不是配置错,而是“没人动它自己坏了”——发行版升级、初始化脚本重置、磁盘挂载点变化都会悄悄改掉配置。我通常把巡检写成一个脚本丢给 cron 每日跑,脚本内容如下:
#!/usr/bin/env bash # 转储配置巡检:检查 core_pattern、进程限制、磁盘空间与 coredump 状态 # 用法:./check_coredump.sh [服务名] SERVICE="${1:-}" echo "== core_pattern 当前值 ==" cat /proc/sys/kernel/core_pattern echo "== 转储目录磁盘空间 ==" df -h /var/crash /var/lib/systemd/coredump 2>/dev/null | awk 'NR==1 || /\//' if [ -n "$SERVICE" ]; then pid=$(systemctl show -p MainPID --value "$SERVICE") if [ "$pid" != "0" ]; then echo "== ${SERVICE} (pid=${pid}) 的 core 限制 ==" grep "Max core file size" /proc/$pid/limits else echo "服务 $SERVICE 未运行" fi fi echo "== 最近 10 条转储记录 ==" coredumpctl list --no-pager 2>/dev/null | head -10脚本三个点值得说明:一是 coredumpctl 在没有安装 systemd-coredump 的机器上会报错,所以加了 2>/dev/null 和 head 限制输出条数;二是 df 命令用 awk 只过滤转储目录相关的行,避免无关挂载点干扰判断;三是传服务名时用 systemd 的 MainPID 拿实际 pid,比手动 ps grep 更可靠。cron 里可以这样写:
10 4 * * * /usr/local/bin/check_coredump.sh nginx >> /var/log/coredump-check.log 2>&1每天凌晨跑一次,输出重定向到日志文件,只要 core_pattern 不再指向预期值,看一眼日志就能发现。
5.4 健康判据:什么样的巡检结果算正常
对巡检输出,我按三个信号判断当前转储配置是否健康:
- core_pattern 非空,且指向绝对路径或真实存在的管道程序;
- 对主要服务,/proc/ /limits 的 Max core file size 是 unlimited;
- 转储目录剩余空间大于计划保留的转储文件总大小的两倍。
这三条全过,才敢说这台机器的转储配置可信。如果只过了前两条但空间不足,反而比不配更危险——core 写一半磁盘满了,和滚雪球一样把其他服务拖垮。所以巡检脚本里我会把磁盘占用单独拎出来,超过阈值直接告警,而不是等转储失败再去翻日志。
6. 进阶:从转储文件里快速挖出崩溃函数
配置和验证都通畅之后,真正要面对的是最后一个问题:core 拿到了,怎么快速从里面挖出崩溃点。我的日常工作流分三步。
第一步,确认二进制与 core 匹配。同一个程序的不同构建版本,core 不能混用;动态库版本变了,gdb 也会报加载错误。先看格式、再解压,最后才打开:
file /var/lib/systemd/coredump/core.nginx.*.zst zstd -d /var/lib/systemd/coredump/core.nginx.*.zst -o /tmp/core.nginx第二步,用 gdb 打开并取栈。对带符号的二进制,这一下就能看到崩溃函数、参数值和寄存器状态:
gdb -q /usr/sbin/nginx /tmp/core.nginx -ex "bt" -ex "info registers rip" -ex "quit"bt 输出的是崩溃瞬间的调用栈,从最新一帧往旧帧看,最上面的函数就是事发地。如果栈被破坏,info registers rip 能告诉你程序计数器最后停在哪条指令。多线程程序的情况更复杂:最先崩溃的往往是某个工作线程,不是主线程,所以我会先执行 thread apply all bt 把全部线程栈导出到文件,再逐个分析。
第三步,把分析流程封装成脚本。我习惯在 ~/.bashrc 里放一个函数,输入程序和 core 文件路径,一键打印崩溃栈:
core_analyze() { local bin="$1" core="$2" file "$core" | grep -q "compressed" && zstd -d "$core" -o /tmp/core.tmp gdb -q "$bin" /tmp/core.tmp -ex "thread apply all bt" -ex "quit" }这套习惯帮我省下无数次“core 文件传回本地却打不开”的返工。最后给一句劝告:core 文件是进程完整内存映像,密码、密钥、用户数据全在里面,转储目录一定要限制权限、定期清理,别把后悔药变成泄密口。配好转储后,按第五章做一次完整验证,让这份《转储配置.pdf》真正成为团队遇到段错误时的第一顺位排查手段。希望帮到你。
本文还有配套的精品资源,点击获取