Linux coredump 不生成?排查链路与修复指南
发布时间:2026/10/19 4:27:20 锦皓数字建站

1. 程序崩溃了coredump 却不见踪影——这个坑到底有多深半夜收到告警服务进程挂了你揉着眼睛登上机器熟练地敲下ls -lh /var/coredump/或者ls core*结果空空如也。日志里只有一句干巴巴的Segmentation fault连个栈回溯都没有。这时候你心里大概会冒出一句core 文件呢被谁吃了这个场景我遇到过太多次了。coredump是 Linux 下排查程序崩溃最直接、最有力的手段之一它把进程崩溃那一刻的完整内存镜像、寄存器状态、调用栈全部落盘配合 gdb 基本能还原出“案发现场”。但现实往往是程序崩得干干净净core 文件却死活不出现。问题不在于内核不给而在于从“进程崩溃”到“core 文件落盘”这条链路上有太多环节可以把它掐断。这篇文章就是把这个链路一节一节拆开告诉你 core 文件到底是怎么产生的、哪些开关会拦住它、每一种拦截场景怎么排查、怎么修复。适合所有在 Linux 上做后端开发、运维、嵌入式调试的同学尤其是那些被“崩溃无 core”折磨过的人。我会从内核参数、进程资源限制、文件系统配置、权限、容器环境、信号处理等多个维度讲透并且给出可以直接抄的排查命令和配置模板。看完之后你应该能做到拿到一台机器五分钟内定位 core 文件为什么没生成并且把它修好。2. 先搞清楚 coredump 的完整生成链路2.1 从信号到文件内核到底做了什么很多人以为进程一崩溃内核就自动写 core其实不是。整个流程大致是这样的进程收到某个信号比如 SIGSEGV、SIGABRT、SIGFPE如果这个信号的默认动作是“终止并产生 core”内核才会进入 core dump 流程。注意这里有个前提——信号的处理动作必须是默认的。如果程序自己用signal()或sigaction()注册了处理函数并且处理函数正常返回或者调用了exit()那内核就不会产生 core。只有程序没有捕获该信号或者捕获后重新把动作设回默认才会走到 core dump。进入 core dump 流程后内核会做几件事检查进程的RLIMIT_CORE资源限制如果为 0 就直接放弃检查 core 文件的命名模板和存放路径检查目标路径所在文件系统的可用空间和挂载参数检查进程是否有权限写入目标文件最后才把内存镜像写出去。任何一个环节不满足core 文件都不会产生而且大多数情况下内核是“静默失败”的不会在系统日志里留下明显痕迹这就是为什么很多人觉得 core 文件“凭空消失”。2.2 为什么默认配置下经常看不到 core现在主流的发行版默认配置对 core dump 其实相当不友好。RLIMIT_CORE在很多系统上默认是 0也就是直接禁止产生 core。即使不是 0kernel.core_pattern默认值可能是core也就是在当前工作目录生成一个叫core的文件但很多服务的WorkingDirectory是/或者某个只读目录根本写不进去。再加上 systemd 管理的服务默认会接管 core dump把它交给systemd-coredump处理文件不再落在你熟悉的路径而是进了/var/lib/systemd/coredump/用coredumpctl才能看到。还有一个容易被忽略的点容器环境。容器里默认的RLIMIT_CORE往往是 0而且容器内的/proc/sys/kernel/core_pattern是宿主机内核的全局配置容器里改不了。所以容器里程序崩溃core 文件要么没有要么落在宿主机上你根本找不到的地方。3. 逐项排查core 文件到底被谁拦住了3.1 第一站确认信号是否真的触发了 core dump不是所有崩溃都会产生 core。SIGSEGV、SIGABRT、SIGFPE、SIGILL、SIGBUS、SIGQUIT、SIGTRAP 这些信号的默认动作是“终止并 core”但 SIGTERM、SIGINT、SIGKILL 不会。SIGKILL 是内核直接杀掉进程连信号处理都不走更不会产生 core。所以第一步是确认进程到底死于哪个信号。用dmesg看内核日志通常会有一行类似traps: 进程名[pid] general protection fault ip:xxx sp:xxx error:0或者segfault at xxx ip xxx sp xxx error 4。这能告诉你是不是段错误。如果日志里只有Killed那多半是 OOM killer 或者被 SIGKILL 干掉的这种情况本来就不会有 core。还有一种情况是程序自己捕获了信号然后调用了abort()abort()会先解除 SIGABRT 的阻塞再重新触发理论上会产生 core但如果程序在abort()之前把RLIMIT_CORE设成了 0或者自己写了 core_pattern 相关的逻辑也会导致没有 core。提示如果dmesg里连段错误信息都没有先确认进程是不是被 OOM killer 杀的。用dmesg | grep -i out of memory或者grep -i oom /var/log/messages查一下。3.2 第二站RLIMIT_CORE 是不是被设成了 0这是最常见的原因没有之一。RLIMIT_CORE是每个进程的资源限制单位是字节0 表示禁止产生 core。查看当前 shell 的限制用ulimit -c如果输出是 0那这个 shell 里启动的程序都不会产生 core。查看某个正在运行的进程的限制用cat /proc/pid/limits | grep core输出类似Max core file size 0 0 bytes。修改方式分几种。临时改当前 shellulimit -c unlimited只对当前 shell 及其子进程生效。永久改某个用户编辑/etc/security/limits.conf加上* soft core unlimited和* hard core unlimited但注意这对 systemd 服务不一定生效因为 systemd 有自己的限制机制。对 systemd 服务要在 unit 文件里加LimitCOREinfinity然后systemctl daemon-reload并重启服务。这里有个坑ulimit -c unlimited在有些 shell 里写unlimited不认得写ulimit -c 0之外的数值或者用ulimit -c unlimited的 bash 内建写法。另外如果 hard limit 是 0普通用户是没法把 soft limit 提上去的必须用 root 改 hard limit。3.3 第三站core_pattern 指向了哪里/proc/sys/kernel/core_pattern决定了 core 文件的命名和存放位置。默认值可能是core表示在当前工作目录生成名为core的文件。但现在的发行版很多把它设成了|/usr/lib/systemd/systemd-coredump %P %u %g %s %t %c %h意思是把 core 交给 systemd-coredump 处理文件不会出现在你预期的目录。查看当前值cat /proc/sys/kernel/core_pattern。如果是管道符|开头说明 core 被转交给用户态程序处理了。这时候你要么去/var/lib/systemd/coredump/找用coredumpctl list和coredumpctl dump pid查看要么把 core_pattern 改回普通路径比如echo /var/coredump/core.%e.%p.%t /proc/sys/kernel/core_pattern。改 core_pattern 要注意如果路径里有目录目录必须存在且可写%e是进程名%p是 pid%t是时间戳%u是 uid%g是 gid%s是信号编号%h是主机名。用这些占位符可以避免多个 core 文件互相覆盖。另外core_pattern 是全局的改了对所有进程生效生产环境改之前要评估影响。注意有些系统上/proc/sys/kernel/core_pattern是只读的或者被 systemd 锁定直接 echo 会报Read-only file system。这种情况需要改/etc/sysctl.conf或者/etc/sysctl.d/下的配置文件加上kernel.core_pattern/var/coredump/core.%e.%p.%t然后sysctl -p生效。3.4 第四站目标目录的权限和空间core 文件要写到哪里那个目录必须对崩溃进程的用户可写。如果 core_pattern 是/var/coredump/core.%e.%p而/var/coredump的属主是 root权限是 755那普通用户进程崩溃时就没法写入core 文件自然没有。解决办法是把这个目录的权限设成 1777带 sticky bit类似 /tmp或者设成特定用户可写。空间问题也很常见。core 文件大小等于进程的内存占用一个占了几 GB 内存的进程core 文件也可能几 GB。如果目标分区空间不够写入会失败。用df -h确认目标分区剩余空间。另外如果 core_pattern 里的路径包含子目录比如/var/coredump/%e/core.%p那%e对应的子目录必须存在内核不会自动创建目录。还有一个隐蔽的坑文件系统挂载参数。如果目标目录所在文件系统挂载时带了noexec、nosuid一般不影响写文件但如果带了ro只读那肯定写不了。更隐蔽的是某些网络文件系统或者特殊文件系统对 core 文件的写入支持不好可能静默失败。3.5 第五站容器和 systemd 的特殊处理容器里排查 core 文件要先确认容器内的ulimit -c。Docker 默认的RLIMIT_CORE是 0需要在docker run时加--ulimit core-1或者在 compose 文件里配ulimits: core: -1。Kubernetes 里要在 Pod 的 securityContext 或者容器的ulimits字段配置但注意 K8s 对 core limit 的支持在不同版本和运行时上表现不一致很多时候需要靠 init 容器或者特权模式来调。容器内的core_pattern是宿主机内核的全局值容器里改不了。如果宿主机 core_pattern 是管道模式core 会被宿主机上的 systemd-coredump 接走文件落在宿主机的/var/lib/systemd/coredump/容器里看不到。这时候要么在宿主机上找要么把宿主机的 core_pattern 改成普通路径并且确保容器内进程有权限写那个路径通常需要把宿主机的目录挂载进容器。systemd 服务的情况前面提过除了LimitCOREinfinity还要注意ProtectSystem、ReadWritePaths这些沙箱选项。如果 unit 文件里设了ProtectSystemstrict或者ReadOnlyPaths/那 core 文件可能写不进去。用systemd-analyze security service可以看服务的沙箱限制把相关路径加到ReadWritePaths里。4. 实战一步步把 core 文件找回来4.1 快速排查清单遇到 core 文件不生成按这个顺序查基本能覆盖 95% 的情况排查项查看命令期望值修复方式信号类型dmesg | grep -i segfault有段错误/abort 记录确认不是 SIGKILL/OOM进程 core 限制cat /proc/pid/limits | grep coreunlimited改 limits.conf 或 unit 文件shell core 限制ulimit -cunlimitedulimit -c unlimitedcore_patterncat /proc/sys/kernel/core_pattern普通路径非管道改 sysctl 配置目标目录权限ls -ld /var/coredump可写chmod 1777 或改属主目标分区空间df -h /var/coredump有足够空间清理空间或换路径容器 core 限制docker inspect或cat /proc/1/limitsunlimited启动参数加 --ulimit core-1systemd 沙箱systemd-analyze security svc无 ReadOnlyPaths 限制加 ReadWritePaths4.2 一个完整的修复示例假设有个服务叫demo-service用 systemd 管理崩溃后没有 core。排查过程如下。先看服务状态和日志systemctl status demo-service看到Main process exited, codekilled, status11/SEGV说明是段错误信号没问题。然后看进程限制cat /proc/$(pidof demo-service)/limits | grep core输出Max core file size 0 0 bytes找到第一个问题——RLIMIT_CORE 是 0。改 unit 文件在[Service]段加LimitCOREinfinity然后systemctl daemon-reload systemctl restart demo-service。再崩溃一次发现还是没有 core。看 core_patterncat /proc/sys/kernel/core_pattern输出|/usr/lib/systemd/systemd-coredump ...说明 core 被 systemd-coredump 接走了。用coredumpctl list看果然有记录用coredumpctl dump pid -o /tmp/core.demo导出即可。如果不想用 systemd-coredump就改 core_pattern新建/etc/sysctl.d/99-coredump.conf写入kernel.core_pattern/var/coredump/core.%e.%p.%t然后mkdir -p /var/coredump chmod 1777 /var/coredump执行sysctl -p /etc/sysctl.d/99-coredump.conf。再崩溃core 文件就出现在/var/coredump/下了。4.3 验证 core 文件是否可用拿到 core 文件后用 gdb 验证gdb /path/to/binary /path/to/core进去后bt看调用栈。如果栈是乱的或者显示??可能是二进制和 core 不匹配版本不一致或者编译时没加-g。生产环境的二进制通常 stripped 了符号需要保留一份带符号的版本用于调试。另外如果程序用了动态库gdb 需要能找到对应的库文件可以用set solib-search-path指定库路径。提示core 文件可能很大传输和存储都占空间。可以在 core_pattern 里用%e和%t区分定期清理旧文件。如果只是临时调试调完就删别长期留着占地方。5. 那些年我踩过的 core 文件坑5.1 坑一ulimit 改了但服务还是没 core这个坑我踩过不止一次。在 shell 里ulimit -c unlimited然后手动启动程序崩溃有 core。但同样的程序用 systemd 启动就是没 core。原因就是 systemd 不读 shell 的 ulimit它有自己的DefaultLimitCORE配置在/etc/systemd/system.conf里。默认值可能是 0所以所有 systemd 服务都继承 0。改这个文件里的DefaultLimitCOREinfinity然后systemctl daemon-reexec所有服务才会生效。或者在单个 unit 文件里加LimitCOREinfinity只对该服务生效。5.2 坑二core_pattern 改了但没生效改/proc/sys/kernel/core_pattern用 echo 重定向当时生效了但重启后又变回去了。因为/proc/sys是内存文件系统重启就丢。正确做法是写进/etc/sysctl.conf或/etc/sysctl.d/下的配置文件。另外有些系统上 systemd-sysctl 服务会覆盖这个值需要确认/etc/sysctl.d/里的配置优先级够高或者直接改 systemd 的sysctl.d配置。5.3 坑三容器里 core 文件落在宿主机上在容器里跑程序崩溃后容器里找不到 core但在宿主机的/var/lib/systemd/coredump/里找到了。这是因为 core_pattern 是宿主机内核的全局配置容器共享同一个内核。解决办法有两个一是把宿主机的 core_pattern 改成普通路径并且把这个路径挂载进容器二是在容器里用coredumpctl从宿主机拿但这需要容器有权限访问宿主机的 systemd 接口比较麻烦。更简单的做法是在容器启动脚本里加ulimit -c unlimited并且把 core_pattern 指向容器内可写的挂载卷。5.4 坑四core 文件写了一半就没了有时候能看到 core 文件但大小是 0 或者很小gdb 打不开。这通常是写入过程中失败了比如磁盘满了、进程被再次信号打断、或者文件系统有配额限制。检查dmesg里有没有core dump to ... failed之类的记录。另外如果进程内存特别大core 文件可能超过文件系统单文件大小限制需要确认文件系统类型和挂载参数。5.5 坑五程序自己把 core 关了有些程序在启动时会主动调用setrlimit(RLIMIT_CORE, 0)来禁止 core比如某些数据库或者对性能敏感的服务。这种情况下不管系统怎么配core 都不会产生。排查方法是看程序源码或者用strace跟踪setrlimit调用。如果是这种情况只能改程序配置或者用其他调试手段比如 gdb attach、eBPF来抓现场。6. 进阶不依赖 core 文件的崩溃排查手段core 文件虽然好用但有时候就是拿不到或者生产环境不允许产生 core比如涉及敏感数据。这时候可以退而求其次用其他手段抓崩溃现场。gdb attach在程序运行时就 attach 上去崩溃时 gdb 会停在信号处可以看栈。缺点是 attach 会暂停进程对高并发服务影响大而且需要提前知道要 attach 哪个进程。eBPF用 bpftrace 或者 bcc 工具抓信号和栈回溯对进程侵入小适合生产环境。比如bpftrace -e tracepoint:signal:signal_generate /sig 11/ { printf(%s %d\n, comm, pid); }可以抓谁收到了 SIGSEGV。systemd-coredump即使 core_pattern 是管道模式systemd-coredump 也会把 core 存下来只是路径不同。用coredumpctl管理可以按时间、pid、进程名过滤比手动管理 core 文件方便。应用层日志在程序里注册信号处理函数收到 SIGSEGV 时用backtrace()打印调用栈到日志。注意信号处理函数里能做的事很有限backtrace()不是异步信号安全的但实践中很多项目这么用配合backtrace_symbols_fd()直接写文件描述符能拿到基本可用的栈。核心转储到内存有些场景下可以把 core 写到内存文件系统tmpfs避免磁盘 IO 和空间问题但重启就丢适合临时调试。7. 把 core dump 配置固化成可复用的方案折腾一圈之后最好把配置固化下来避免每次换机器都要重新排查。我的做法是写一个初始化脚本在新机器或者新容器里跑一遍把 core dump 相关配置全部设好。脚本大概长这样#!/bin/bash # 开启 core dump设置路径和命名规则 mkdir -p /var/coredump chmod 1777 /var/coredump # 写入 sysctl 配置 cat /etc/sysctl.d/99-coredump.conf EOF kernel.core_pattern/var/coredump/core.%e.%p.%t kernel.core_uses_pid1 EOF sysctl -p /etc/sysctl.d/99-coredump.conf # 设置全局 core 限制 cat /etc/security/limits.d/99-coredump.conf EOF * soft core unlimited * hard core unlimited EOF # systemd 全局限制 mkdir -p /etc/systemd/system.conf.d cat /etc/systemd/system.conf.d/99-coredump.conf EOF [Manager] DefaultLimitCOREinfinity EOF systemctl daemon-reexec echo coredump 配置完成core 文件将写入 /var/coredump/容器里的话在 Dockerfile 或者启动脚本里加ulimit -c unlimited并且确保/var/coredump是挂载卷。K8s 里可以用 initContainer 做这些事或者用 privileged 模式跑一个配置容器。注意生产环境开启 core dump 要评估磁盘空间和敏感数据风险。core 文件包含进程完整内存可能有密钥、用户数据等敏感信息要控制访问权限定期清理必要时加密存储。8. 最后分享几个实用小技巧第一个技巧用coredumpctl的时候coredumpctl gdb pid可以直接把 core 加载进 gdb不用手动找文件路径省事。coredumpctl info pid可以看崩溃的基本信息包括信号、栈回溯如果 systemd 版本够新。第二个技巧如果 core 文件太大可以在 gdb 里用generate-core-file重新生成一个精简的或者用gcore对运行中的进程抓 core不用等它崩溃。第三个技巧排查 core 文件不生成的时候用strace -f -e tracewrite,openat跟踪进程看它有没有尝试写 core 文件以及失败的原因。不过 strace 对性能影响大只适合调试环境。第四个技巧如果怀疑是 SELinux 或 AppArmor 拦住了 core 写入看/var/log/audit/audit.log或者dmesg里的 avc denied 记录。SELinux 下可能需要给 core 目录打上合适的标签或者设semanage fcontext。第五个技巧core 文件的命名模板里%e在有些内核版本上对长进程名会截断%p是 pid%t是时间戳。如果同一进程频繁崩溃用%t可以避免覆盖。但注意%t是 Unix 时间戳看起来不直观可以用%t配合date -d timestamp转换。这些经验都是实际踩坑踩出来的希望能帮你少走点弯路。core 文件这东西平时用不上真需要的时候没有那才叫抓瞎。提前配好关键时刻能救命。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。