我最近在 Ubuntu LTS 服务器上创建了一项服务,允许我测试网络的正常运行时间。脚本本身按预期工作,但是当我尝试将其变成服务时,我遇到了一些问题,服务一启动就自行停止(通过命令 stop)。
控制文件:
#!/bin/bash
case "$1" in
start)
/home/user/network_script/network_script.sh &
echo $!>/var/run/network_script.pid
;;
stop)
kill `cat /var/run/network_script.pid`
rm /var/log/network_output.status
rm /var/run/network_script.pid
;;
restart)
$0 stop
$0 start
;;
status)
if [ -e /var/run/network_script.pid ]; then
echo network_script.sh is running, pid=`cat /var/run/network_script.pid`
echo /var/log/network_script.status
else
echo network_script.sh is NOT running
exit 1
fi
;;
*)
echo "Usage: $0 {start|stop|status|restart}"
esac
exit 0
系统文件:
[Unit]
Description=Network Script
[Service]
ExecStart=/home/user/network_script/network_script_controls.sh start
ExecStop=/home/user/network_script/network_script_controls.sh stop
ExecRestart=/home/user/network_script/network_script_controls.sh restart
ExecStatus=/home/user/network_script/network_script_controls.sh status
[Install]
WantedBy=multi-user.target
状态输出:
network_script.service - Network Script
Loaded: loaded (/etc/systemd/system/network_script.service; enabled; vendor preset: enabled)
Active: inactive (dead) since Tue 2023-01-03 14:07:46 EST; 8s ago
Process: 25131 ExecStart=/home/user/network_script/network_script_controls.sh start (code=exited, status=0/S>
Process: 25133 ExecStop=/home/user/network_script/network_script_controls.sh stop (code=exited, status=0/SUC>
Main PID: 25131 (code=exited, status=0/SUCCESS)
CPU: 9ms
Jan 03 14:07:46 greengoblin systemd[1]: Started Network Script.
Jan 03 14:07:46 greengoblin network_script_controls.sh[25136]: rm: cannot remove '/var/log/network_output.status>
Jan 03 14:07:46 greengoblin systemd[1]: network_script.service: Deactivated successfully.
答案1
添加Type=forking
到您的[Service]
部分。,
来自手册页:
如果设置为 forking,则预计使用 ExecStart= 配置的进程将调用 fork() 作为其启动的一部分。当启动完成并且所有通信通道都建立后,父进程预计将退出。子进程继续作为主服务进程运行,当父进程退出时,服务管理器会认为该单元已启动。这是传统 UNIX 服务的行为。如果使用此设置,建议同时使用PIDFile=选项,以便systemd能够可靠地识别服务的主进程。一旦父进程退出,systemd 将继续启动后续单元。
此外,最好让 systemd 了解您的 PID 文件,以便它可以确定进程是否仍在运行或崩溃:
手册页是这样说的PIDFile=
:
采用引用服务的 PID 文件的路径。对于 Type= 设置为分叉的服务,建议使用此选项。指定的路径通常指向 /run/ 下面的文件。如果指定了相对路径,则其前缀为 /run/。服务启动后,服务管理器会从该文件中读取该服务主进程的PID。服务管理器不会写入此处配置的文件,但如果该文件仍然存在,它会在服务关闭后删除该文件。 PID 文件不需要由特权用户拥有,但如果它由非特权用户拥有,则会强制执行附加安全限制:该文件可能不是指向其他用户拥有的文件的符号链接(无论是直接还是间接) ,并且 PID 文件必须引用已经属于该服务的进程。
请注意,现代项目中应避免使用 PID 文件。尽可能使用 Type=notify 或 Type=simple,这样不需要使用 PID 文件来确定服务的主进程,并避免不必要的分叉。
这意味着您的服务应该如下所示:
[Service]
Type=forking
PIDFile=/var/run/network_script.pid
ExecStart=/home/user/network_script/network_script_controls.sh start
ExecStop=/home/user/network_script/network_script_controls.sh stop
ExecRestart=/home/user/network_script/network_script_controls.sh restart
ExecStatus=/home/user/network_script/network_script_controls.sh status
也就是说,仔细看看最后一段PIDFile=
。由于您使用的是 systemd,因此您不需要自己跟踪 PID。这使得你的分叉脚本变得不必要。 Systemd 会为您自动执行此操作。
我会放弃/home/user/network_script/network_script_controls.sh
并使用这个单元文件:
[Unit]
Description=Network Script
[Service]
Type=simple
ExecStart=/home/user/network_script/network_script.sh
[Install]
WantedBy=multi-user.target