Workerman 本身无C扩展,所谓“底层崩溃”实为混用swoole等扩展所致;需先禁用可疑扩展验证,再通过ulimit、GDB和调试符号定位具体C函数问题。

Workerman 本身是纯 PHP 实现的,不带 C 扩展;如果你遇到“Workerman 底层 C 扩展崩溃”,实际大概率是混用了其他扩展(如 swoole、event、redis、protobuf 或自研扩展),或在 Workerman 进程中加载了触发底层内存问题的 PHP 扩展。GDB 调试的关键不是 Workerman,而是那个真正引发 Segmentation fault (core dumped) 的 C 扩展。
确认崩溃是否真由 C 扩展引起
别急着开 GDB——先排除纯 PHP 层误判:
- 运行
php --ri workerman,确认输出里没有Extension compiled with或compiled modules等 C 扩展痕迹 - 检查
php -m列出的已启用扩展,重点关注swoole、event、amqp、grpc等常见易崩溃扩展 - 临时禁用可疑扩展:在
php.ini中注释掉extension=swoole.so等行,再启动 Workerman,看是否还崩溃 - 如果崩溃消失,说明问题在该扩展;如果仍崩溃,检查是否启用了
opcache+jit(PHP 8.1+),尝试关闭opcache.enable_jit=0
生成并加载有效的 core 文件
Workerman 多进程模型下,core 文件容易被丢弃或路径错乱:
- 启动前执行
ulimit -c unlimited,否则系统不会生成 core - Workerman 子进程默认继承父进程限制,但某些发行版(如 Ubuntu)有
apport拦截,需临时停用:sudo systemctl stop apport - core 文件名可能含 PID,比如
core.php.12345,用ls -t core*找最新一个 - 用
gdb $(which php) core.php.12345加载,不是gdb workerman(Workerman 没有可执行文件) - 若提示
No symbol table,说明你用的是无调试符号的 PHP —— 必须换装php3.10-dbg(Ubuntu)或重编译带--with-pydebug的 PHP(注意:这里是 PHP,不是 Python;标题里的“PyTorch”是知识库噪声,勿混淆)
定位到具体扩展的 C 函数调用点
GDB 加载 core 后,bt full 输出常卡在 zend_execute 或 zif_*,需要人工穿透:
- 执行
bt,找到最靠近顶部的非 Zend 内部帧,例如:#4 0x00007f9a1b2c34d2 in zim_swoole_http_response_end (execute_data=0x7f9a2c012020, return_value=0x7f9a2c012010) at /path/to/swoole/src/ext/http/response.cc:128 - 若显示
??,说明该 so 没调试符号:用readelf -n $(php-config --extension-dir)/swoole.so | grep BUILD_ID查 build-id,再确认系统是否安装对应swoole-dbgsym包(Ubuntu)或从源码重新编译make clean && make -j$(nproc) DEBUG=1 - 关键变量检查:
print *(zval*)$rdi(假设崩溃在第一个 zval 参数)、info registers看rax是否为0x0(空指针解引用) - 若怀疑
zval生命周期问题,加断点到zval_ptr_dtor或zval_delref_p,观察 refcount 变为 0 后是否还被访问
绕过 Workerman 封装,最小化复现崩溃
Workerman 的事件循环会掩盖时序敏感问题,直接调用扩展函数更易复现:
- 写一个极简 PHP 脚本,只做触发动作,例如:
<?php $redis = new Redis(); $redis->connect('127.0.0.1'); $redis->get('nonexistent'); - 用
php -d extension=redis.so crash.php替代php start.php start -d,避免 Workerman 进程管理干扰 - 若仍崩溃,说明问题与 Workerman 无关,是扩展自身或环境(如 libc 版本、TLS 库冲突)导致
- 此时可配合
ASan:重新编译扩展时加-fsanitize=address -fno-omit-frame-pointer,运行时报错会直接指出heap-use-after-free行号
真正难的不是 GDB 命令,而是区分「Workerman 场景下暴露的问题」和「扩展自身缺陷」——很多所谓“Workerman 崩溃”,本质是扩展在高并发短连接场景下才暴露出的 refcount 竞态或内存释放顺序错误,这类问题在单次脚本里未必复现,得结合 gdb attach 到正在运行的子进程,再发请求触发。


















