我正在尝试组建一个脚本,它将启动我的(Node)开发服务器,每当它收到SIGHUP
时,它应该重新启动服务器。
我已经产生了服务器,关闭它并在SIGHUP
上重新启动服务器。但是因为我在产卵代码中使用wait
,SIGHUP
处理程序永远不会真正返回,这会导致信号再也不会被触发。
这是我的脚本的精简版本:
SERVER_PID=""
start_server() {
npm start &
SERVER_PID=$!
wait $SERVER_PID
}
terminate_server() {
[ ! "xSERVER_PID" = "X" ] && kill -SIGTERM $SERVER_PID
SERVER_PID=""
}
refresh_server() {
terminate_server
start_server
}
trap refresh_server SIGHUP
start_server
正如我所提到的,它启动服务器正常,并且在第一个SIGHUP
上按预期工作,但由于refresh_server
中的wait
,start_server
永远不会返回,后续信号不会触发任何动作。
目前,我已经通过取出wait
中的start_server
并在底部添加无限“while-true-sleep”循环(在初次调用{之后)解决了这个问题{1}}),但我确信必须有更好的方法来实现我想要实现的目标。此外,我不喜欢睡眠循环方法引起的信号触发延迟。
答案 0 :(得分:0)
while-true-wait循环怎么样?
#!/bin/bash
SERVER_PID=""
SERVER_NAME="npm start"
start_server() {
$SERVER_NAME &
SERVER_PID=$!
SERVER_ACTIVE=true
}
terminate_server() {
[ ! "xSERVER_PID" = "X" ] && kill -SIGTERM $SERVER_PID
SERVER_PID=""
}
refresh_server() {
terminate_server
start_server
}
trap refresh_server SIGHUP
start_server
while $SERVER_ACTIVE; do
SERVER_ACTIVE=false
wait $SERVER_PID
done
需要某种等待事件循环,如果它应该重复,要么在脚本中显式重复,要么在bash中隐藏的地方重复
答案 1 :(得分:0)
与在其他编程语言中使用信令一样,信号捕获可以以错误的方式轻松完成;
使用trap
时,您必须不处理您的功能内部陷阱评估,但只设置标志 < em> main 程序可以在陷阱异常结束后进行检查,以便尽可能缩短异常执行。
特别是,您必须不在陷阱执行级别向子进程发起fork
!
#!/bin/bash
SERVER_PID=""
CMD_TRAP=""
npm() { #Doing something that could be checked from external
if [ "$1" ] && [ "$1" == "start" ] ;then
while :;do
date "+%s%N" >/tmp/dummyfile.txt
sleep .333
done
fi
}
start_server() {
npm start &
SERVER_PID=$!
}
terminate_server() {
[ "$SERVER_PID" ] && ps $SERVER_PID &>/dev/null && kill -TERM $SERVER_PID
SERVER_PID=""
}
refresh_server() {
terminate_server
start_server
}
printf "for:\n server restart, hit: 'kill -USR2 %d'\n" $$
printf " server stop, hit: 'kill %d' (or Ctrl+C)\n" $$
trap 'CMD_TRAP=refresh' USR2 HUP
trap 'CMD_TRAP=terminate' TERM INT
start_server
while [ "$SERVER_PID" ];do
wait $SERVER_PID
case "$CMD_TRAP" in
refresh ) refresh_server ;;
terminate ) terminate_server ;;
* ) refresh_server ;; # in case server just end.
esac;
CMD_TRAP=""
[ "$SERVER_PID" ] && echo "LOOP." || echo "EXIT."
done
此演示脚本执行:
USR2
或HUP
信号,TERM
信号,Ctrl-C
被安装在控制台上。的窗口1 强> 的
tty
/dev/pts/0
的窗口2 强> 的
ps --tty pts/0 fw
PID TTY STAT TIME COMMAND
2996 pts/0 Ss 0:01 bash
5187 pts/0 S+ 0:00 \_ bash
的窗口1 强> 的
./serverScript.sh
for:
server restart, hit: 'kill -USR2 11469'
server stop, hit: 'kill 11469' (or Ctrl+C)
的窗口2 强> 的
ps --tty pts/0 fw
PID TTY STAT TIME COMMAND
2996 pts/0 Ss 0:01 bash
5187 pts/0 S 0:00 \_ bash
11469 pts/0 S+ 0:00 \_ /bin/bash ./servermon.sh
11470 pts/0 S+ 0:00 \_ /bin/bash ./servermon.sh
cat /tmp/dummyfile.txt
1361603642256133674
cat /tmp/dummyfile.txt
1361603648712606114
ps --tty pts/0 fw
PID TTY STAT TIME COMMAND
2996 pts/0 Ss 0:01 bash
5187 pts/0 S 0:00 \_ bash
11469 pts/0 S+ 0:00 \_ /bin/bash ./servermon.sh
11470 pts/0 S+ 0:01 \_ /bin/bash ./servermon.sh
16814 pts/0 S+ 0:00 \_ sleep .333
kill -USR2 11469
的窗口1 强> 的
LOOP.
的窗口2 强> 的
ps --tty pts/0 fw
PID TTY STAT TIME COMMAND
2996 pts/0 Ss 0:01 bash
5187 pts/0 S 0:00 \_ bash
11469 pts/0 S+ 0:00 \_ /bin/bash ./servermon.sh
17152 pts/0 S+ 0:00 \_ /bin/bash ./servermon.sh
17532 pts/0 S+ 0:00 \_ sleep .333
cat /tmp/dummyfile.txt
1361604208069564188
cat /tmp/dummyfile.txt
1361604209103660589
kill -USR2 11469
的窗口1 强> 的
LOOP.
的窗口2 强> 的
cat /tmp/dummyfile.txt
1361604278583723517
cat /tmp/dummyfile.txt
1361604279605292149
kill 11469
的窗口1 强> 的
EXIT.
$
窗口1终止循环和服务器子进程。
的窗口1 强> 的
./serverScript.sh
for:
server restart, hit: 'kill -USR2 19232'
server stop, hit: 'kill 19232' (or Ctrl+C)
然后按 Ctrl + C :
的窗口1 强> 的
^CEXIT.
$
的窗口2 强> 的
ps --tty pts/0 fw
PID TTY STAT TIME COMMAND
2996 pts/0 Ss 0:01 bash
5187 pts/0 S+ 0:00 \_ bash
无法叠加异常。因此,当执行异常时,可以忽略另一个中断:
如果对于示例,您的refresh_server()
函数必须在重新启动服务器之前压缩和旋转某些日志,这可能会变得很重要:
refresh_server() {
terminate_server
lockedfilename=-$(date +%F_%H-%M-%S-$$)
mv /srv/logfile /srv/logfile-$lockedfilename
gzip /srv/logfile-$lockedfilename
start_server
}
许多中断可能在主循环中被sumarized或忽略,但处理必须仅在主级别完成。
有一个关于错误的小演示:
的窗口1 强> 的
trap "echo USR2 sleep 4;sleep 4" USR2
while :;do printf "\r%s " $(date +%s%N);sleep .333;done
1361606565xxxxxxxxx
(xxxxxxxxx
改变3x /秒)
的窗口2 强> 的
for ((i=5;i--;));do echo KILL;kill -USR2 5187;sleep .5;done
KILL
KILL
KILL
KILL
KILL
1361606722582175969 USR2 sleep 4
USR2 sleep 4
1361606770xxxxxxxxx
我的循环中只有两个中断超过五个。
如果您搜索解释:5 x 0,5 = 2,5秒。睡眠时间远不到4秒,为什么我接到第二个中断,但不是全部五个?
在主循环中有一个小技巧:
while [ "$SERVER_PID" ];do
wait $SERVER_PID
OLDIFS="$IFS" IFS=$'\n'
TRAPS=($(trap)) # save traps
IFS="$OLDIFS"
eval "$(printf "trap -- %s\n" ${TRAPS[@]##*\'})" # untrap
case "$CMD_TRAP" in
refresh ) refresh_server ;;
terminate ) terminate_server ;;
* ) refresh_server ;; # in case server just end.
esac;
CMD_TRAP=""
if [ "$SERVER_PID" ]
then echo "LOOP."
else echo "EXIT."
eval "$(printf "%s\n" "${TRAPS[@]}")" # restore traps
fi
done
答案 2 :(得分:0)
这是一个带有尾递归start_server的解决方案,只对您的代码进行了少量更改。 (删除了一行并添加了三行包含HUPPED
)
HUPPED=false
SERVER_PID=""
start_server() {
npm start &
SERVER_PID=$!
wait $SERVER_PID
if $HUPPED; then HUPPED=false; start_server; fi
}
terminate_server() {
[ ! "xSERVER_PID" = "X" ] && kill -SIGTERM $SERVER_PID
SERVER_PID=""
}
refresh_server() {
HUPPED=true
terminate_server
#start_server
}
trap refresh_server SIGHUP
start_server