1. 崩溃现象与环境初探
1.1 观察崩溃表现
当 PHP 内核突然崩溃 时,最直观的表现往往是网页返回 500 错误、相关进程异常退出,甚至出现 core dump(核心转储)文件。同时日志中可能出现 segfault、信号中断或进程重启等信息。这些线索是后续定位的第一步。请将崩溃发生的时间点、触发的请求路径、涉及的 PHP 版本和 Web 服务器记录下来,作为排查的起点。
要点提示:关注“谁在崩、何时崩、何种请求触发、是否伴随 core 文件”这四个要素,它们决定后续的诊断方向。
为了快速了解当前环境,可以查看几项关键版本信息与运行状态,如下所示的命令可以快速给出线索:
uname -a
php -v
php -i | head -n 50
ps aux | grep -E 'php-fpm|php-fpm7|php-cgi' | grep -v grep
1.2 收集环境信息与运行参数
系统与 PHP 的版本、扩展模块、以及 OPcache 等缓存是否开启,都会影响崩溃的发生概率。记录操作系统版本、内核版本、PHP 的编译选项、以及加载的扩展,这为后续定位“是不是某个扩展或版本特定问题”提供了依据。
下面的命令帮助你快速聚焦哪些模块和配置可能与崩溃相关:
php -i | grep -E "PHP Version|Loaded Configuration File|extension|opcache|error_log"
php -m
2. 启用并定位核心转储(core dump)
2.1 启用核心转储并确保策略生效
核心转储是定位崩溃的关键证据。要确保系统在崩溃时能生成 core 文件,需同时开启系统级和服务级别的限制。先设置系统允许生成无限制大小的 core 文件,再调整 core_pattern,使核心转储落在可访问的位置。
下面的步骤包含了修改系统参数、以及为服务创建的带有无限制 CORE 的运行环境的做法:
# 设置 core_pattern,便于定位
echo 'kernel.core_pattern = core.%e.%p.%h.%t' | sudo tee /etc/sysctl.d/99-core-pattern.conf
sudo sysctl --system
# 允许生成核心转储
ulimit -c unlimited
# 针对 systemd 服务,显式设置无限制的 CORE
sudo systemctl edit php-fpm
# 在编辑器中添加:
# [Service]
# LimitCORE=infinity
# 然后保存并退出
sudo systemctl daemon-reload
完成后,重新启动相关服务以使新设置生效:
sudo systemctl restart php-fpm
2.2 验证核心转储已生成的路径与文件
生成核心转储的文件通常会落在 /var/crash、/cores、或根据 kernel/core_pattern 指定的位置。你可以在触发崩溃后快速搜索最近的 core 文件:
ls -l /var/crash || ls -l /cores || ls -l / | grep -i core
find / -name 'core.*' -type f 2>/dev/null | head -n 20
如果没有看到核心转储,需回退检查 core_pattern 与 文件权限,确保运行进程有权限写入核心文件。
2.3 使用调试器读取核心转储
核心转储是定位崩溃原因的重要证据。通过 gdb 可以快速获得调用栈信息,帮助你找到崩溃发生的具体代码位置。
基本的调试流程是:加载可执行文件和核心转储,获取回溯信息;如果需要,可以继续查看每个线程的调用栈和寄存器状态。
gdb -q /usr/bin/php /path/to/core.1234 -ex "bt" -ex "quit"
也可以先只查看最简单的回溯:
gdb -q /usr/bin/php /path/to/core.1234 -ex "bt"
3. 查看日志与系统信息
3.1 系统级日志排查
系统日志往往记录了与崩溃相关的内核事件、资源限制变更、或进程信号。通过 journalctl 可以快速筛选出与崩溃时间吻合的记录段落,结合关键字如 php、fpm、segfault、core 等,定位线索。
执行下列命令,聚焦最近的崩溃时段:
sudo journalctl -xe --since="2025-01-01 00:00:00" | grep -i -E 'php|fpm|segfault|core'
3.2 应用日志与错误日志
PHP 本身的错误日志、FPM 日志、以及 Web 服务器日志,是判断崩溃原因最重要的资料源。定位到崩溃前后的日志上下文,能帮助你分辨是权限、内存、扩展、还是穷举性错误导致的崩溃。
常见日志位置包括:/var/log/php-fpm/error.log、/var/log/nginx/error.log、以及 php.ini 指定的 error_log 位置。现在快速查看最近 200 行日志:
tail -n 200 /var/log/php-fpm/error.log
tail -n 200 /var/log/nginx/error.log
grep -Rni "segfault" /var/log/php* /var/log/nginx* 2>/dev/null | head -n 50
3.3 使用 dmesg 获取内核级信息
内核层面的崩溃可能由硬件、内核模块或驱动引起。dmesg 提供的最近日志能帮助你确认是否有与进程崩溃相关的内核事件。
dmesg -T | tail -n 200
4. 使用调试工具诊断崩溃
4.1 进程级调试:strace 与 perf
如果崩溃发生在执行某些系统调用时,strace 可以记录进程在崩溃前后的系统调用序列,帮助定位资源缺失、权限问题或系统调用错误。
对待排查的进程进行跟踪,关注内存、文件、信号等事件的调用情况:
sudo strace -p -e trace=memory,file,signal -f -s 200
同时,perf 可以用来进行轻量级的性能和崩溃相关分析,定位热点函数和执行路径。
perf top -p &
4.2 使用 gdb 深度分析核心转储
对于复杂崩溃,直接用 gdb 对应的可执行文件和 core 文件进行深入分析,是最直接的手段。通过回溯栈和变量信息,可以锁定崩溃位置及调用关系。
gdb -q /usr/bin/php /path/to/core.1234
(gdb) bt full
(gdb) info locals
(gdb) quit
5. 快速定位至具体代码位置的策略
5.1 根据核心转储回溯定位代码区段
结合 bt(backtrace) 输出,可以确定崩溃发生在 PHP 的哪个模块、哪一行附近。将回溯中的地址映射到源码位置,是快速定位的关键步骤。
若你有符号化的可执行文件和库,回溯信息将更完整,便于直接定位到源码文件与行号。
# 在 gdb 中完成回溯后,定位到具体源码位置的示例
(gdb) info frame 0
(gdb) frame 1
5.2 使用 addr2line 将地址映射到源码
当回溯给出地址而非源码行号时,可以借助 addr2line 将地址映射回源码位置。确保使用与运行时一致的符号库版本,这对快速定位至可疑代码段非常重要。
addr2line -e /usr/bin/php 0x00007f8a3b1c2d4a
5.3 针对扩展与模块的排查策略
若崩溃可能来自某个 PHP 扩展或二进制库,按如下策略排查:逐步禁用扩展、逐步回归到稳定版本、对比变更历史,并结合日志中的异常点锁定目标模块。
# 例:禁用某扩展后重启
echo "extension=edge.so" >> /etc/php/7.4/fpm/php.ini
systemctl restart php-fpm
以上步骤构成了从崩溃现象到具体定位的一步步方法。通过系统日志、应用日志、核心转储以及调试工具的综合分析,你可以快速找出导致 PHP 内核崩溃 的根本原因,并定位到具体的位置与条件。


