是什么在拖延我的 systemd 用户计时器?

What is stalling my systemd user timer?

我使用 systemd 用户计时器代替 cron。我有一个特定的程序设置为每 20 分钟执行一次。该程序不是守护进程,依赖于网络,并启动许多子进程。但是我注意到计时器经常在几个小时(或几天)后停止。计时器仍处于活动状态,但程序不再每 20 分钟执行一次。 pgrep 显示许多进程仍处于活动状态。观察到这一点后,我将 JobTimeoutSec=3m 添加到 .service 文件中,期望进程在超时时会被杀死。

systemctl status --user PROGRAM.service 现在输出以下内容,但是子进程仍然 运行 并且计时器不再每 20 分钟执行一次程序:

Feb 13 15:03:45 HOSTNAME systemd[1878]: Job PROGRAM.service/start timed out.

Feb 13 15:03:45 HOSTNAME systemd[1878]: Timed out starting DESCRIPTION.

Feb 13 15:03:45 HOSTNAME systemd[1878]: Job PROGRAM.service/start failed with result 'timeout'.

我猜测程序的子进程由于网络问题而停止,systemd 无法在超时时终止它们。

有任何解决此问题的建议,以便计时器按预期继续运行吗?

ExecStart=/usr/bin/timeout 20m /path/to/program 替换 ExecStart=/path/to/program 似乎可以解决这个问题,但我想找出为什么 systemd 单独没有。


调试信息

PROGRAM.service

[Unit]
Description=DESCRIPTION
After=network.target
PartOf=network-online.target
JobTimeoutSec=3m

[Service]
Type=oneshot
ExecStart=/path/to/program

[Install]
WantedBy=network-online.target

PROGRAM.timer

[Unit]
Description=Run PROGRAM.service every 20 minutes

[Timer]
OnCalendar=*:0/20

[Install]
WantedBy=timers.target

systemd --version 输出如下:

systemd 219

+PAM +AUDIT +SELINUX +IMA +APPARMOR +SMACK +SYSVINIT +UTMP +LIBCRYPTSETUP +GCRYPT -GNUTLS +ACL +XZ -LZ4 -SECCOMP +BLKID -ELFUTILS +KMOD -IDN

活动进程

systemd 中有两件重要的事情,我认为你在这种情况下会遇到:

  1. 当您使用 systemd 启动进程时,所有 child 进程(至少在默认情况下)都是同一组的一部分。

  2. 如果其中任何一个children没有死亡,则认为该过程仍然(至少在某种程度上)运行ning。

什么意思?

timer description 说:

Note that in case the unit to activate is already active at the time the timer elapses it is not restarted, but simply left running.

换句话说,如果您的任何一个进程在 20 分钟后仍然 运行ning,定时器系统将不会重新启动任何东西。

为什么这是有道理的?!

CRON 也在做同样的事情。如果您的进程仍在 运行ning,它不会一遍又一遍地重新启动它(因为那只会填满内存并可能破坏许多其他东西。)但是,CRON 没有进程组的概念。因此,如果您的主进程确实死了,它假定它可以重新启动它。

systemd解决方案是什么?

假设你不能只停止 child 进程(虽然因为你使用了 /usr/bin/timedout,你可能可以?),一种方法是使用 KillMode 选项,虽然我不推荐:

KillMode=process

这意味着一旦主进程挂掉,就认为服务停止了。

If set to process, only the main process itself is killed.

您可能想测试这是否真的有效,因为根据文档,它没有说它会认为整个组都死了...但根据我的经验,这有效。

那什么是更好的解决方案呢?

既然我不推荐KillMode,应该还有别的解决办法。事实是,您的所有进程要么有 20 分钟 运行(或者它们生成时剩余的任何时间),要么它们将阻止以下 运行 发生,一旦一段时间,但如果他们永远留在身边,肯定不会。因此,将编辑这些进程并确保它们在一段时间后退出。

但是,在 long 一段时间后,可能有必要终止这些进程,如果进程本身不能,那么使用您所做的超时工具可能是最好的解决方案只是 准时 退出。尽管我会建议一个小的修改,即使用 19 分钟。超时,否则你可能会错过下一次启动 window.

ExecStart=/usr/bin/timeout 19m /path/to/program