systemd通过bash脚本调用应用时无法正确上报段错误问题
问题
我有一个通过bash脚本在systemd下运行的服务,systemd报告该服务因段错误崩溃。我重现了调用流程:
第一种配置(通过bash脚本启动)
api.service
[Unit] Description=API After=network.target httpd.service DAEMON_COREFILE_LIMIT="unlimited" [Install] WantedBy=default.target [Service] Type=forking Restart=on-failure ExecStart=/tmp/tmp.YEkmibLAJq TimeoutStartSec=360
tmp.YEkmibLAJq(bash脚本):
#!/bin/bash echo 'int main() { *(int *)0 = 0; }' > /tmp/seg.c && gcc -o /tmp/segfault /tmp/seg.c /tmp/segfault
执行systemctl start api后,应用发生段错误,但systemd未正确上报段错误:
$ journalctl -u api api.service: Control process exited, code=exited, status=139/n/a
第二种配置(直接启动段错误程序)
api.service
[Unit] Description=API After=network.target httpd.service DAEMON_COREFILE_LIMIT="unlimited" [Install] WantedBy=default.target [Service] Type=forking Restart=on-failure ExecStart=/tmp/segfault TimeoutStartSec=360
日志显示:
$ journalctl -u api api.service: Control process exited, code=dumped, status=11/SEGV
请问这是否意味着bash脚本引发了段错误?或者有没有办法让子进程的段错误状态传递给systemd?我认为bash脚本不太可能引发段错误。
解答
不是bash脚本引发的段错误,实际是bash启动的子进程
/tmp/segfault发生了段错误。systemd显示的status=139其实就是段错误的退出码(128+11=139,其中11是SIGSEGV的信号编号),只是因为systemd监控的是bash进程而非子进程,所以没有显示code=dumped和status=11/SEGV的详细信息。要让子进程的段错误状态传递给systemd,可以通过以下几种方式:
- 让bash脚本用exec替换自身为目标进程:修改bash脚本,把
/tmp/segfault改成exec /tmp/segfault。这样bash进程会被目标进程替换,systemd监控的进程变成目标进程,段错误会被正确识别并上报。
修改后的脚本:#!/bin/bash echo 'int main() { *(int *)0 = 0; }' > /tmp/seg.c && gcc -o /tmp/segfault /tmp/seg.c exec /tmp/segfault - 调整systemd服务的Type:如果不需要forking模式,可以把
Type=forking改成Type=simple,但仍推荐结合exec方式让systemd直接监控目标进程。 - 使用systemd的
ExecStartPre做预处理:如果脚本仅用于启动前准备,可把编译步骤放到ExecStartPre中,ExecStart直接启动目标程序。示例:[Service] Type=forking Restart=on-failure ExecStartPre=/bin/bash -c "echo 'int main() { *(int *)0 = 0; }' > /tmp/seg.c && gcc -o /tmp/segfault /tmp/seg.c" ExecStart=/tmp/segfault TimeoutStartSec=360
- 让bash脚本用exec替换自身为目标进程:修改bash脚本,把
内容的提问来源于stack exchange,提问作者glades
相关产品推荐
相关产品推荐

