man*_*tor 7 process systemd daemon
我已经根据这个答案的代码创建了一个 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
Run Code Online (Sandbox Code Playgroud)
就在 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
Run Code Online (Sandbox Code Playgroud)
如果我echo $$ > /run/daemon.pid;在脚本的开头插入 PID creaton 语句(子级和父级也将使用它),我会收到以下警告:
daemon.service: PID 30631 read from file /run/daemon.pid does not exist or is a zombie.
Run Code Online (Sandbox Code Playgroud)
创建 PID 文件而 systemd 没有收到任何警告消息的最佳方法是什么?
fil*_*den 12
所以你在这里看到的问题是因为Type=forking在使用时,必须在父进程退出之前创建 pid 文件(使用正确的 pid)。
如果您从子进程创建 pidfile,那么它将与父进程的退出竞争,并且在某些(许多?)情况下会导致您看到的第一个错误。
如果您$$在启动子进程之前创建 pidfile 写入它,那么它将具有已退出的父进程的 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
Run Code Online (Sandbox Code Playgroud)
这应该可以正常工作......但是,有一个更好的方法来实现这一点!继续阅读...
实际上,systemd 的全部意义在于守护进程并为您在后台运行它们……通过尝试自己做,您只是在阻止 systemd 为您做这件事。这同时让你的生活变得更加艰难......
而不是使用Type=forking,只需编写您的 shell 脚本以在前台运行并设置要使用的服务Type=simple。那么你不需要任何pidfiles。
更新您/root/bin/daemon.sh只需执行以下操作:
#!/bin/bash
# Run the following code in foreground:
while keep_running; do
do_something
done
Run Code Online (Sandbox Code Playgroud)
(注意: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
Run Code Online (Sandbox Code Playgroud)
顺便说一句,你可能会删除ExecStop=,因为用信号杀死进程也是默认行为......
systemdType=forking确实只适用于只能以这种方式工作且无法轻松修复以在前台工作的遗留程序......它很笨拙且效率低下。systemd(以及它的一些替代品,前辈)的全部意义在于自己进行分叉和守护进程,让服务只担心做他们需要做的事情!:-)
我希望这对您有帮助...我真的希望您选择让 systemd 为您完成繁重的工作!这样效率更高。
好的,daemon.service: PID file /run/daemon.pid not readable (yet?) after start: No such file or directory我在服务文件中添加以下语句后错误信息消失了:
ExecStartPost=/bin/sleep 2
Run Code Online (Sandbox Code Playgroud)