我在嵌入式系统中使用 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
从文档(系统服务和系统服务),我希望如果我foo_app
以触发的方式杀死Restart=on-abnormal
(例如killall -9 foo_app
), systemd 应该优先考虑Restart=on-abnormal
overOnFailure=systemd-reboot.service
而不是 start systemd-reboot.service
。
然而,这不是我所看到的。只要我杀foo_app
一次,系统就会立即重新启动。
以下是文档中的一些相关片段:
OnFailure=
当该单元进入“失败”状态时激活的一个或多个单元的以空格分隔的列表。仅在达到启动限制后,使用 Restart= 的服务单元才会进入故障状态。
Restart=
[剪]请注意,服务重新启动受 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
.
不寻常的是,systemdfoo.service: Unit entered failed state.
每当foo_app
被杀死时都会打印,即使它即将通过 重新启动Restart=on-abnormal
。这似乎与上面引用的文档中的这些行直接矛盾:
仅在达到启动限制后,使用 Restart= 的服务单元才会进入故障状态。
仅在达到启动限制后,重新启动的服务才会进入失败状态。
所有这一切都让我很困惑。我是否误解了这些 systemd 选项?这是系统错误吗?任何帮助表示赞赏。
答案1
2019/08/12 编辑:根据therealjumbo
的评论,此问题的修复已与systemd
v239 合并并发布,因此,如果您由于发行版而未固定到某个版本(看着你的 CentOS)更新并快乐起来!
TL;DR - 已知文档问题,目前仍然是该systemd
项目的一个悬而未决的问题
事实证明,自从你问这个问题以来,这件事已经被报道了,被认定为差异介于systemd
文档和实际行为之间。根据我的理解(以及我对 github 问题的阅读),你的期望和文档相符,所以你并不疯狂。
目前,systemd
无论是否已达到启动限制,每次尝试启动后都会将状态设置为失败。在本期中,OP 写了一个关于学习骑自行车的有趣轶事,我强烈建议您看一下。