定期的にタスクを実行し、別のプロセスからUSR1シグナルを受信するシェルスクリプトを書いています。
スクリプトの構造は this answer に似ています:
#!/bin/bash
trap 'echo "doing some work"' SIGUSR1
while :
do
sleep 10 && echo "doing some work" &
wait $!
done
ただし、このスクリプトには、スリーププロセスがバックグラウンドで続行され、タイムアウトでのみ終了するという問題があります。 (待機$!中にUSR1が受信されると、スリーププロセスは通常のタイムアウトの間残りますが、定期的なエコーは実際にキャンセルされます。)たとえば、pkill -0 -c sleep
を使用して、マシン上のスリーププロセスの数を確認できます。
私は このページ を読みました。
#!/bin/bash
pid=
trap '[[ $pid ]] && kill $pid; echo "doing some work"' SIGUSR1
while :
do
sleep 10 && echo "doing some work" &
pid=$!
wait $pid
pid=
done
ただし、USR1信号を高速でスパムする場合、このスクリプトには競合状態があります。と:
pkill -USR1 trap-test.sh; pkill -USR1 trap-test.sh
次に、すでに強制終了されたPIDを強制終了しようとし、エラーを出力します。言うまでもなく、このコードは好きではありません。
中断されたときにフォークされたプロセスを確実に強制終了するより良い方法はありますか?または、同じ機能を実現するための代替構造?
子を含むプロセスツリー全体を強制終了し、それを適切に強制終了しようとする関数を使用することをお勧めします。これがスクリプトに追加できる部分です。
TrapQuitは、SIGUSR1または受信した他の終了信号(CTRL + Cを含む)で呼び出されます。 TrapQuitで必要な処理を追加するか、通常のスクリプト終了時に終了コードを使用して呼び出すことができます。
# Kill process and children bash 3.2+ implementation
# BusyBox compatible version
function IsInteger {
local value="${1}"
#if [[ $value =~ ^[0-9]+$ ]]; then
expr "$value" : "^[0-9]\+$" > /dev/null 2>&1
if [ $? -eq 0 ]; then
echo 1
else
echo 0
fi
}
# Portable child (and grandchild) kill function tested under Linux, BSD, MacOS X, MSYS and cygwin
function KillChilds {
local pid="${1}" # Parent pid to kill childs
local self="${2:-false}" # Should parent be killed too ?
# Paranoid checks, we can safely assume that $pid should not be 0 nor 1
if [ $(IsInteger "$pid") -eq 0 ] || [ "$pid" == "" ] || [ "$pid" == "0" ] || [ "$pid" == "1" ]; then
echo "CRITICAL: Bogus pid given [$pid]."
return 1
fi
if kill -0 "$pid" > /dev/null 2>&1; then
# Warning: pgrep is not native on cygwin, must be installed via procps package
if children="$(pgrep -P "$pid")"; then
if [[ "$pid" == *"$children"* ]]; then
echo "CRITICAL: Bogus pgrep implementation."
children="${children/$pid/}"
fi
for child in $children; do
KillChilds "$child" true
done
fi
fi
# Try to kill nicely, if not, wait 15 seconds to let Trap actions happen before killing
if [ "$self" == true ]; then
# We need to check for pid again because it may have disappeared after recursive function call
if kill -0 "$pid" > /dev/null 2>&1; then
kill -s TERM "$pid"
if [ $? != 0 ]; then
sleep 15
kill -9 "$pid"
if [ $? != 0 ]; then
return 1
fi
else
return 0
fi
else
return 0
fi
else
return 0
fi
}
function TrapQuit {
local exitcode="${1:-0}"
KillChilds $SCRIPT_PID > /dev/null 2>&1
exit $exitcode
}
# Launch TrapQuit on USR1 / other signals
trap TrapQuit USR1 QUIT INT EXIT