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}"
uuidgen
myTagを、または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
}