我有一个web服务器,在那里我作为守护进程运行一些启动缓慢的程序。当我重新编译它们或切换到另一个安装时,这些有时需要快速重新启动(或停止)。
灵感来自http://mywiki.wooledge.org/ProcessManagement,我在写剧本称为daemonise.sh
,看起来像
#!/bin/sh
while :; do
./myprogram lotsadata.xml
echo "Restarting server..." 1>&2
done
以保持"守护进程"运行。因为我有时需要阻止它,或者只是重新启动它,我在屏幕会话中运行该脚本,比如:
$ ./daemonise.sh & DPID=$!
$ screen -d
然后,也许我重新编译我的程序,将其安装到一个新路径,然后启动新的一个,并想杀死旧的:
$ screen -r
$ kill $DPID
$ screen -d
当我是唯一的维护者时,这很好,但现在我想让其他人停止/重新启动程序,无论是谁启动的为了使事情变得更加复杂,事实上daemonise.sh
脚本启动大约16个程序,这使得杀死每一个程序都很麻烦如果你不知道他们的PID。
让另一个用户使用的"最佳做法"是什么是否停止/重新启动守护程序?
我想过共享屏幕会话,但这听起来很古怪缺乏信心的我现在想出的最好的解决方案是在捕捉特定信号的脚本中启动和终止:
#!/bin/bash
DPID=
trap './daemonise.sh & DPID=$!' USR1
trap 'kill $DPID' USR2 EXIT
# Ensure trapper wrapper doesn't exit:
while :; do
sleep 10000 & wait $!
done
现在,如果另一个用户需要停止守护程序,而我无法完成,她只需要知道包装器的pid,例如sudo kill -s
USR2 $wrapperpid
。(此外,这使运行守护程序成为可能重新启动时,仍然可以干净地杀死它们。)
有更好的解决方案吗?这有明显的问题吗我没有看到的解决方案?
(在阅读了Greg的Bash Wiki之后,我想避免任何涉及pgrep或PID文件的解决方案…)
我推荐一个基于PID的init脚本。任何拥有sudo脚本特权的人都可以启动和停止服务器进程。
关于改进您的方法:如果您的pidwapper脚本以某种方式退出,那么确保sleep 10000 & wait $!
中的sleep
命令正确终止是否可取?
否则,在相当长的一段时间内,进程表中将保留一个挂起的sleep
进程。
同样,在重新启动时(即,如果daemonise.sh
接收到TERM
信号)正确地终止daemonise.sh
中的myprogram
不是更干净吗?
此外,可以抑制作业通知消息,并在终止前测试pid是否存在。
#!/bin/sh
# cat daemonise.sh
# cf. "How to suppress Terminated message after killing in bash?",
# http://stackoverflow.com/q/81520
trap '
echo "server shut down..." 1>&2
kill $spid1 $spid2 $spid3 &&
wait $spid1 $spid2 $spid3 2>/dev/null
exit
' TERM
while :; do
echo "Starting server..." 1>&2
#./myprogram lotsadata.xml
sleep 100 &
spid1=${!}
sleep 100 &
spid2=${!}
sleep 100 &
spid3=${!}
wait
echo "Restarting server..." 1>&2
done
#------------------------------------------------------------
#!/bin/bash
# cat pidwrapper
DPID=
trap '
kill -0 ${!} 2>/dev/null && kill ${!} && wait ${!} 2>/dev/null
./daemonise.sh & DPID=${!}
' USR1
trap '
kill -0 ${!} 2>/dev/null && kill ${!} && wait ${!} 2>/dev/null
kill -0 $DPID 2>/dev/null && kill $DPID && wait ${DPID} 2>/dev/null
' USR2
trap '
trap - EXIT
kill -0 $DPID 2>/dev/null && kill $DPID && wait ${DPID} 2>/dev/null
kill -0 ${!} 2>/dev/null && kill ${!} && wait ${!} 2>/dev/null
exit 0
' EXIT
# Ensure trapper wrapper does not exit:
while :; do
sleep 10000 & wait $!
done
#------------------------------------------------------------
# test
{
wrapperpid="`exec sh -c './pidwrapper & echo ${!}' | head -1`"
echo "wrapperpid: $wrapperpid"
for n in 1 2 3 4 5; do
sleep 2
# start daemonise.sh
kill -s USR1 $wrapperpid
sleep 2
# kill daemonise.sh
kill -s USR2 $wrapperpid
done
sleep 2
echo kill $wrapperpid
kill $wrapperpid
}