创建 shell 脚本守护进程时,哪个 PID 属于 systemd PIDFile 部分?

创建 shell 脚本守护进程时,哪个 PID 属于 systemd PIDFile 部分?

我创建了一个 shell 脚本守护进程,基于以下代码这个答案。我编写了一个包含以下内容的 systemd 服务文件:

[Unit]
Description=My Daemon
After=network.target

[Service]
Type=forking
PIDFile=/run/daemon.pid
ExecStart=/root/bin/daemon.sh
ExecReload=/bin/kill -1 -- $MAINPID
ExecStop=/bin/kill -- $MAINPID
TimeoutStopSec=5
KillMode=process

[Install]
WantedBy=multi-user.target

就在 while 循环开始之前,我正在创建一个 PID 文件(它将由守护进程创建,而不是由子进程或父进程创建,因为它们还没有达到这一点):echo $$ > /run/daemon.pid;.它工作正常,但每次我打电话时systemctl status daemon.service,我都会收到以下警告:

daemon.service: PID file /run/daemon.pid not readable (yet?) after start: No such file or directory

如果我在脚本的最开头插入 PID 创建语句echo $$ > /run/daemon.pid;(子级和父级也会使用该语句),我会收到以下警告:

daemon.service: PID 30631 read from file /run/daemon.pid does not exist or is a zombie.

创建 PID 文件而不收到 systemd 任何警告消息的最佳方法是什么?

答案1

所以你在这里看到的问题是因为当Type=forking使用时,必须在父进程退出之前创建pid文件(使用正确的pid)。

如果您从子进程创建 pidfile,那么它将与父进程的退出竞争,并且在某些(很多?)情况下会导致您看到的第一个错误。

如果您在启动子进程之前创建写入它的 pid 文件$$,那么它将具有已退出的父进程的 pid,因此您将看到另一个错误。

正确执行此操作的一种方法是在退出之前从父级写入 pidfile。在这种情况下, write $!(而不是$$),它返回后台生成的最后一个进程的 pid。

例如:

#!/bin/bash

# Run the following code in background:
(
    while keep_running; do
        do_something
    done
) &

# Write pid of the child to the pidfile:
echo "$!" >/run/daemon.pid
exit

这应该可以正常工作...然而,有更好的方法来实现这一点!继续阅读...


实际上,systemd 的全部意义在于守护进程并在后台为您运行它们...如果您尝试自己执行此操作,您只是在阻止 systemd 为您执行此操作。这同时让你的生活变得更加困难......

不用使用Type=forking,只需编写 shell 脚本来运行在前景中并设置要使用的服务Type=simple。那么你就不需要任何pid文件了。

更新你的/root/bin/daemon.sh以简单地执行此操作:

#!/bin/bash

# Run the following code in foreground:
while keep_running; do
    do_something
done

(注意:daemon.sh此时可能不是最好的名称...因为这意味着它在后台运行。也许可以将其命名为更合适的名称,与它实际执行的操作相关。)

然后更新.service要使用的文件Type=simple(实际上这里默认使用该文件,因此您甚至可以省略它。)

[Service]
Type=simple
ExecStart=/root/bin/daemon.sh
ExecReload=/bin/kill -1 -- $MAINPID
ExecStop=/bin/kill -- $MAINPID
TimeoutStopSec=5
KillMode=process

顺便说一句,您可能可以删除ExecStop=,因为用信号杀死进程也是默认行为......

systemdType=forking确实只适用于只能以这种方式工作的遗留程序,并且无法轻松修复以在前台工作......它很糟糕且效率低下。 systemd(以及它的一些替代品、前身)的全部意义在于自行进行分叉和守护进程,让服务只关心做他们需要做的事情! :-)

我希望您觉得这很有帮助...并且我真的希望您选择让 systemd 为您完成繁重的工作!这样效率就高多了。

答案2

daemon.service: PID file /run/daemon.pid not readable (yet?) after start: No such file or directory好的,在我将以下语句添加到服务文件后,错误消息消失了:

ExecStartPost=/bin/sleep 2

相关内容