PID番号を使用してプロセスを終了するときに誤ったプロセスの終了を防ぐ方法は?

PID番号を使用してプロセスを終了するときに誤ったプロセスの終了を防ぐ方法は?

Bashスクリプトで長期実行バックグラウンドプロセスを開始します。プロセスをバックグラウンドで送信した後、PID番号を変数に保存し、必要に応じてそのPID番号を使用してプロセスを終了します。

ただし、スクリプトがバックグラウンドプロセスを終了する前にバックグラウンドプロセスが終了し、システムが新しく作成されたプロセスに同じPID番号を割り当てる場合は、その番号を使用してバックグラウンドプロセスを終了するときに機能します。新しく作成されたプロセスを終了します(権限によって異なります)。 、もちろん)。

使用されたPID番号は短期間に新しく作成されたプロセスに割り当てられていませんが、私のスクリプトは数週間実行されるため可能です。

そのような事故をどのように避けることができますか?

答え1

コメントで提案されているように、このpkillユーティリティは役に立ちます。

「bashスクリプト」と言ったので、実行する必要がありますpkill bash。これはすべきではありません。

pkill -f <name>代わりに、完全なプロセス名を使用してマッチングを使用できます。したがって、タスクがあると仮定すると、bash /home/me/my_script.sh次のものを使用できます。

pkill -f -e my_script.sh

これは-eオプションで、何が死んだのかを出力します。


選択する:

次のスクリプトを別の名前で/usr/bin/mykill(または希望の場所に)保存します。

#!/bin/bash
mypid="$1"
if [[ ! -f /proc/$mypid/cmdline ]]; then
    echo "Process ID not found."
    exit 1
else
    echo "About to kill $(cat /proc/$mypid/cmdline)"
    echo "Press enter if you want to kill that process"
    read -p "Press CTRL-C if you don't want that"
    kill $mypid
fi

次のように実行します。mykill <pid>

答え2

バックグラウンドプロセスを制御できる場合は、追加のIDをコマンドラインにラベルとして追加し、Pidでコピーをアーカイブしてからチェックインできますps -o args myPid

私はこのようなオプションを使う--unique "${myTag}"

uuidgenmyTagを、またはaからdateナノ秒精度でエクスポートします。 SSHジョブの場合は、ローカルホスト名を含めます。

新しいオプションを導入できない場合:

date +%s.. ジョブの開始時間を取得するために使用され、Pid とともに保存されます。

..ps -o etimesプロセスの経過時間を秒単位で取得するために使用されます。

..現在と比較するとdate +%s(数秒程度差が出ることがあります)。

どちらの方法も、Pidと組み合わせると無視できるエラー確率を持つ必要があります。

答え3

間違ったPIDが削除されることを心配することなく、PIDを/ proc / <PID>のタイムスタンプと組み合わせて一意のIDにしました。

$PID 保存:

echo $PID $(stat --format %Z /proc/$PID/comm) > pid

$PIDを安全に終了します。

read PID TIMESTAMP < pid
[[ $(stat --format %Z /proc/$PID/comm) != $TIMESTAMP ]] || kill -SIGKILL $PID

これにより、他のプロセスで$ PIDを回収しても、作成時間(/ proc / $ PID / commのタイムスタンプ)が異なるため、複製できません。

PS:これはtrueの場合は何もしません。そうでなければ実行を[[ ... ]] || cmd意味します。[[ ]]cmd

編集:他の多くの回避策がありますが、解決するべきではない理由は何ですか?これは、バックグラウンドサービスを必要とせず、ほとんどのコンテナでうまくサポートされていないsystemdなどの高度なシステムライブラリを必要としない最も簡単で直接的な方法でなければなりません。私は進行中の他のプロセスを中止する必要がある製品にこのアプローチを使用しました。

EDIT2:バックグラウンドプロセスを終了するときは、別のプロセスグループ(たとえば)からバックグラウンドプロセスを開始することをお勧めしますsetsid background_process &。次に、background_processの子プロセスからプロセスグループIDを取得してプロセスグループIDを取得できますps -o pgid= $$。その後、キラーエンドキルプロセスグループIDでは、すべてのサブプロセスがアトミックにシャットダウンされます。それ以外の場合は、通常のプロセスIDを終了し、そのサブプロセスはまだ生きており、pkill -P parant_pidそれを使用しても新しいサブプロセスがエスケープする可能性があります。

答え4

私はこれが一般的に不可能であることに同意しません。pkill回避できる場合は、名前で殺すことをお勧めしません(特定のコマンドの複数のインスタンスが別々のタイムアウトを持つように合法的にしたい場合はどうなりますか?)。問題の答えがjobs競合状態をどのように排除し、原子的でないかをjobs理解していませんkill

ただし、ジョブ制御が有効になっている場合はプロセスグループID(s)を使用し、オプションでない場合はサブシェルをPGID使用してプロセス親ID()で終了できます。PPID私の投稿を参照してください:https://unix.stackexchange.com/a/649320/464414

好みの方法を抜粋してここに貼り付けます。注意事項と代替案については上記の記事をご覧ください。

更新:次のバージョンはパイプラインで動作します。

timeOut() {
    checkArgs() { [ $(( ${1} )) -gt 0 -a "${*:2}" ]; }
    jobControlEnabled() { expr "${-}" : '.*m' >/dev/null; }
    terminalFDs() { [ -t 0 -a -t 1 ]; }
    groupLeader() { sh -c 'expr `ps -o pgid= ${PPID}` : "${PPID}" >/dev/null;'; }
    timeOutImpl() {
        groupLeader || { echo "Job control error - not group leader!"; return -1; }
        KILL_SUB="kill -- -`sh -c 'echo ${PPID}'`"
        { sleep ${1}; ${KILL_SUB}; } &
        "${@:2}"; ${KILL_SUB}
    }
    checkArgs "${@}" || { echo "Usage: timeOut <delay> <command>"; return -1; }
    if jobControlEnabled && terminalFDs; then
        ( timeOutImpl "${@}"; )
    else
        ( set -m; ( timeOutImpl "${@}"; ); )
    fi
}

関連情報