我在嵌入式系统中使用 systemd 231,我正在尝试创建一个服务来监视系统中的硬件组件。这是对我正在尝试做的事情的粗略描述:
- 当服务 ,
foo.service
启动时,它会启动一个应用程序 ,foo_app
。 foo_app
监控硬件组件,持续运行。- 如果
foo_app
检测到硬件故障,它会以返回码 1 退出。这应该会触发系统重新启动。 - 如果
foo_app
崩溃,systemd 应该重新启动foo_app
。 - 如果
foo_app
反复崩溃,systemd 应该重新启动系统。
这是我将其作为服务实现的尝试:
[Unit]
Description=Foo Hardware Monitor
# If the application fails 3 times in 30 seconds, something has gone wrong,
# and the state of the hardware can't be guaranteed. Reboot the system here.
StartLimitBurst=3
StartLimitIntervalSec=30
StartLimitAction=reboot
# StartLimitAction=reboot will reboot the box if the app fails repeatedly,
# but if the app exits voluntarily, the reboot should trigger immediately
OnFailure=systemd-reboot.service
[Service]
ExecStart=/usr/bin/foo_app
# If the app fails from an abnormal condition (e.g. crash), try to
# restart it (within the limits of StartLimit*).
Restart=on-abnormal
从文档(systemd.service和systemd.service)来看,我希望如果我以一种触发foo_app
的方式杀死(例如), systemd 应该优先考虑over而不是 start 。Restart=on-abnormal
killall -9 foo_app
Restart=on-abnormal
OnFailure=systemd-reboot.service
systemd-reboot.service
然而,这不是我所看到的。只要我杀foo_app
了一次,系统就会立即重新启动。
以下是文档中的一些相关片段:
OnFailure=
当该单元进入“失败”状态时激活的一个或多个单元的空格分隔列表。只有在达到启动限制后,使用 Restart= 的服务单元才会进入失败状态。
Restart=
[snip]请注意,服务重启受限于使用 StartLimitIntervalSec= 和 StartLimitBurst= 配置的单元启动速率限制,有关详细信息,请参阅 systemd.unit(5)。只有在达到启动限制后,重新启动的服务才会进入失败状态。
文档看起来很清楚:
- 中指定的服务
OnFailure
应仅在服务进入“failed
”状态时运行 - 一个服务只有在满意后才应该进入“
failed
”状态。StartLimitIntervalSec
StartLimitBurst
这不是我所看到的。
为了确认这一点,我将我的服务文件编辑为以下内容:
[Unit]
Description=Foo Hardware Monitor
StartLimitBurst=3
StartLimitIntervalSec=30
StartLimitAction=none
[Service]
ExecStart=/usr/bin/foo_app
Restart=on-abnormal
通过删除OnFailure
和设置StartLimitAction=none
,我能够看到 systemd 如何应对foo_app
死亡。这是一个我反复杀死的foo_app
测试SIGKILL
。
[root@device ~]
# systemctl start foo.service
[root@device ~]
# journalctl -f -o cat -u foo.service &
[1] 2107
Started Foo Hardware Monitor.
[root@device ~]
# killall -9 foo_app
foo.service: Main process exited, code=killed, status=9/KILL
foo.service: Unit entered failed state.
foo.service: Failed with result 'signal'
foo.service: Service hold-off time over, scheduling restart.
Stopped foo.
Started foo.
[root@device ~]
# killall -9 foo_app
foo.service: Main process exited, code=killed, status=9/KILL
foo.service: Unit entered failed state.
foo.service: Failed with result 'signal'
foo.service: Service hold-off time over, scheduling restart.
Stopped foo.
Started foo.
[root@device ~]
# killall -9 foo_app
foo.service: Main process exited, code=killed, status=9/KILL
foo.service: Unit entered failed state.
foo.service: Failed with result 'signal'
foo.service: Service hold-off time over, scheduling restart.
Stopped foo.
foo.service: Start request repeated too quickly
Failed to start foo.
foo.service: Unit entered failed state.
foo.service: Failed with result 'start-limit-hit'
这是有道理的或大部分。当foo_app
被杀死时,systemd 会重新启动它,直到StartLimitBurst
被击中然后放弃。这就是我想要的,除了StartLimitAction=reboot
.
不寻常的是,systemd 会在被杀死foo.service: Unit entered failed state.
时打印foo_app
,即使它即将通过Restart=on-abnormal
. 这似乎与上面引用的文档中的这些行直接矛盾:
只有在达到启动限制后,使用 Restart= 的服务单元才会进入失败状态。
只有在达到启动限制后,重新启动的服务才会进入失败状态。
这一切都让我很困惑。我是否误解了这些系统选项中的任何一个?这是系统错误吗?任何帮助表示赞赏。
编辑 2019/08/12: Per
therealjumbo
的评论,对此的修复已与systemd
v239 合并并发布,因此,如果您由于您的发行版(看着您的 CentOS)而没有被固定到某个版本,那就开心吧!TL;DR - 已知的文档问题,目前仍是该
systemd
项目的未决问题事实证明,自从您提出这个问题以来,这已被报告并确定为
systemd
文档与实际行为之间的差异。根据我的理解(以及我对 github 问题的阅读),您的期望和文档匹配,所以您并不疯狂。当前
systemd
在每次尝试启动后将状态设置为失败,无论是否已达到启动限制。在这个问题中,OP 写了一个关于学习骑自行车的有趣轶事,我强烈建议您看看。