web-dev-qa-db-ja.com

USR1シグナル後のスリーププロセスを確実に強制終了

定期的にタスクを実行し、別のプロセスから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を強制終了しようとし、エラーを出力します。言うまでもなく、このコードは好きではありません。

中断されたときにフォークされたプロセスを確実に強制終了するより良い方法はありますか?または、同じ機能を実現するための代替構造?

5
jameh

子を含むプロセスツリー全体を強制終了し、それを適切に強制終了しようとする関数を使用することをお勧めします。これがスクリプトに追加できる部分です。

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
0
Orsiris de Jong