我创建了一个 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