一次Python 服务 SIGILL Core Dump 排查
一次 Python 服务 SIGILL Core Dump 排查:underthesea_core 的 CPU 指令集兼容问题
背景
线上 Python 服务启动后异常退出,导出了一个 core 文件:
/tmp/core.91
core 文件对应的进程命令是:
python manage.py --workers 1
服务本身是一个 FastAPI/Gunicorn 应用,业务代码里使用了 underthesea 做越南语分词:
import underthesea
underthesea.word_tokenize(text, format='text')
依赖版本中锁定了:
underthesea==9.2.1
underthesea_core==3.0.5
初步现象
用 file 查看 core:
file /tmp/core.91
可以看到它是一个 Python 进程的 core dump。
用 gdb 加载后,关键信息是:
Program terminated with signal SIGILL, Illegal instruction.
这里不是常见的 SIGSEGV,而是 SIGILL。这通常意味着进程执行了当前 CPU 不支持的机器指令。
定位崩溃模块
继续查看进程内存映射,发现崩溃地址落在:
/usr/local/lib/python3.11/site-packages/underthesea_core/underthesea_core.cpython-311-x86_64-linux-gnu.so
也就是说,崩溃点不在 Python 解释器本身,而是在 underthesea_core 这个 native 扩展模块里。
core 中的 .so build-id 是:
6a2647a5d2329e32aee24ea906fddb00d0dc44f2
随后用本地 conda 环境复现:
/usr/local/miniconda3/envs/content-manager/bin/python -c "import underthesea_core"
结果直接触发 SIGILL。这说明问题和具体业务请求无关,只要导入这个 native 模块就会崩。
关键证据:非法 CPU 指令
反汇编崩溃位置后,看到类似指令:
insertq $0x18,$0x20,%xmm2,%xmm1
在另一次直接 import 复现时,也看到:
vpermpd $0x50,%ymm0,%ymm1
线上机器 CPU 是老款 Intel Xeon E5,lscpu 里只有 avx,没有 avx2,也不支持 AMD SSE4a。
因此:
insertq 在这台 Intel CPU 上不支持
vpermpd 属于 AVX2 指令,这台 CPU 也不支持
这就解释了为什么进程会收到 SIGILL。
一开始的误区:manylinux 不等于 CPU 通用
underthesea_core==3.0.5 在 PyPI 上确实有 wheel:
underthesea_core-3.0.5-cp311-cp311-manylinux_2_34_x86_64.whl
但下载后发现,它的 build-id 和 core 中崩溃的 .so 完全一致:
6a2647a5d2329e32aee24ea906fddb00d0dc44f2
也就是说,线上用的就是 PyPI 这个 wheel。
这里容易误解:manylinux 主要保证的是 Linux/glibc ABI 兼容,不保证 wheel 里的 native 代码没有使用高版本 CPU 指令。
所以这个 wheel 虽然是 manylinux_2_34_x86_64,但它仍然可能包含当前 CPU 不支持的 AVX2/SSE4a 指令。
为什么 3.1.3 可以
继续测试 underthesea_core 后续版本,发现:
3.0.5 有 wheel,但 import 崩溃
3.1.3 有 manylinux2014 wheel,import 正常
3.1.6+ 也正常
3.1.3 的 wheel 是:
underthesea_core-3.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
测试命令:
PYTHONPATH=/tmp/underthesea_core_3.1.3 \
python -c "import underthesea_core; import underthesea; print(underthesea.word_tokenize('VinFast VF 8 gia bao nhieu', format='text'))"
输出正常:
VinFast VF 8 gia_bao nhieu
同时反汇编没有再扫到前面导致崩溃的 insertq / vpermpd。
根因
根因是:
underthesea_core==3.0.5 的 PyPI wheel 使用了当前线上 CPU 不支持的机器指令,
导致 Python 进程在导入 native 扩展时触发 SIGILL。
这不是业务代码异常,也不是 Python 版本问题,而是 native wheel 的 CPU 指令集兼容问题。
解决方案
最小改动是把依赖从:
underthesea_core==3.0.5
升级到最近可用且验证通过的版本:
underthesea_core==3.1.3
安装时建议强制使用 wheel,避免在远程打包机上源码编译:
python -m pip install --no-cache-dir --only-binary=underthesea-core -r requirements.py
如果必须保留 3.0.5,则不能使用 PyPI 上的 wheel,需要自行源码编译,并显式指定通用 CPU:
RUSTFLAGS="-C target-cpu=x86-64" \
python -m pip install --no-cache-dir --no-binary=underthesea-core underthesea_core==3.0.5
经验总结
这次排查的几个关键点:
SIGILL 优先考虑 CPU 指令集不兼容,而不是普通 Python 异常。
native Python 包的 .so 要看 build-id、反汇编和 CPU flags。
manylinux 不等于“所有 x86_64 CPU 都能跑”。
远程打包机器 CPU 比线上新时,要警惕 target-cpu=native。
对 Rust/C/C++ 扩展包,最好优先使用经过验证的通用 wheel。
不断学习

浙公网安备 33010602011771号