生成服务器并等待,在SIGHUP上重复

时间:2013-02-19 18:07:27

标签: bash shell signals

我正在尝试组建一个脚本,它将启动我的(Node)开发服务器,每当它收到SIGHUP时,它应该重新启动服务器。

我已经产生了服务器,关闭它并在SIGHUP上重新启动服务器。但是因为我在产卵代码中使用waitSIGHUP处理程序永远不会真正返回,这会导致信号再也不会被触发。

这是我的脚本的精简版本:

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中的waitstart_server永远不会返回,后续信号不会触发任何动作。

目前,我已经通过取出wait中的start_server并在底部添加无限“while-true-sleep”循环(在初次调用{之后)解决了这个问题{1}}),但我确信必须有更好的方法来实现我想要实现的目标。此外,我不喜欢睡眠循环方法引起的信号触发延迟。

3 个答案:

答案 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)

bash下的信号陷阱

从例外

返回

与在其他编程语言中使用信令一样,信号捕获可以以错误的方式轻松完成;

使用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

功能

此演示脚本执行:

  • 服务将在脚本启动时启动,
  • 如果收到USR2HUP信号,
  • 服务将重新启动,
  • 如果
  • 服务刚刚完成或收到任何信号,
  • 将重新启动服务 如果收到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