アプリケーションを書いています。さまざまな外部プロセスを生成する機能があります。アプリケーションを閉じるときに、アプリケーションが生成されたすべてのプロセスを強制終了してください。
簡単そうですね。私のPIDを調べて、プロセスツリーを再帰的に歩き、見えているすべてをボトムアップスタイルで殺します。
ただし、これはworkではありません。特定のケースでは、foo
を生成しますが、foo
はbar
を生成し、すぐに終了してbar
を実行したままにします。 bar
がアプリケーションのプロセスツリーの一部であったという事実がレコードなしになりました。したがって、アプリケーションはbar
を強制終了する必要があることを知る方法がありません。
私が地球上でこれをやろうとする最初の人物にはなれないと確信しています。では、標準的なソリューションは何ですか?プロセスを生成するプロセスが無条件に同じタグを継承するような方法でプロセスに「タグを付ける」ための方法を本当に探していると思います。
(これまでのところ、私が思いつくことができる最善の方法は、別のユーザーとしてアプリケーションを実行することです。そうすることで、そのユーザーに属するすべてのプロセスを無差別に強制終了できます。しかし、これにはあらゆる種類のアクセス許可の問題があります...)
これは、質問をより注意深く読んだ方がいいと思われる質問の1つです(この質問に対するほとんどの回答がそうであるように見えますが)。質問の要点を明らかに逃していても、いくつかの良い情報が得られるため、元の回答はそのままにしておきます。
ここで最も一般的で堅牢なアプローチ(少なくともLinuxの場合)は、PPIDまたはPGIDではなくSID(セッションID)を使用することだと思います。これは子プロセスによって変更される可能性ははるかに低く、シェルスクリプトの場合、setsid
コマンドを使用して新しいセッションを開始できます。シェルの外では、setuid
システムコールを使用できます。
セッションリーダーであるシェルの場合、次のようにしてセッション内の他のすべてのプロセスを強制終了できます(シェルはそれ自体を強制終了しません)。
_kill $(ps -s $$ -o pid=)
_
注:引数_pid=
_の末尾の等号は、PID
列ヘッダーを削除します。
それ以外の場合は、システムコールを使用して、各プロセスに対してgetsid
を呼び出すことが唯一の方法のようです。
これは最も堅牢なアプローチですが、欠点はLinuxのみであり、root権限が必要なことです。また、シェルツール(使用されている場合)は非常に新しく、広く利用できません。
PID名前空間の詳細については、次の質問を参照してください- `nsenter:`を使用して子プロセスをjailする信頼できる方法 。ここでの基本的なアプローチは、clone
システムコールで_CLONE_NEWPID
_フラグを使用して(またはunshare
コマンドを使用して)、新しいPID名前空間を作成できることです。
PID名前空間のプロセスが孤立すると(つまり、親プロセスが終了すると)、init
ではなく、最上位のPID名前空間プロセスの親になります。つまり、プロセスツリーをたどることにより、常に最上位プロセスのすべての子孫を特定できます。シェルスクリプトの場合、以下のPPIDアプローチはすべての子孫を確実に殺します。
PID名前空間の詳細:
pkill
が利用可能であれば、シェルスクリプトでこれを行う簡単な方法は次のとおりです。
_pkill -P $$
_
これにより、現在の特定のプロセスのすべての子が強制終了されます(_$$
_は、現在のシェルのPIDに展開されます)。
pkill
が利用できない場合、POSIX互換の方法は次のとおりです。
_kill $(ps -o pid= --ppid $$)
_
別の状況では、直接の子だけでなく、現在のシェルプロセスのすべての子孫を強制終了したい場合があります。この場合、以下の再帰的なシェル関数を使用して、すべての子孫PIDをリストしてから、それらをkillの引数として渡します。
_list_descendants ()
{
local children=$(ps -o pid= --ppid "$1")
for pid in $children
do
list_descendants "$pid"
done
echo "$children"
}
kill $(list_descendants $$)
_
注意が必要なことの1つは、上記が期待どおりに機能しない可能性があることです。二重fork()
テクニックです。これは、プロセスをデーモン化するときに一般的に使用されます。名前が示すように、開始されるプロセスは、元のプロセスの2番目のフォークで実行されます。プロセスが開始されると、最初のフォークが終了し、プロセスが孤立します。
この場合、元のプロセスではなくinit
プロセスの子になります。どのプロセスが元の親であったかを特定する確実な方法はないため、これが当てはまる場合、他の特定の手段(PIDファイルなど)がないとプロセスを強制終了することはできません。ただし、この手法を使用した場合は、正当な理由なしにプロセスを強制終了しないでください。
参考文献:
以下を使用できます。
kill -TERM -- -XXX
ここで、XXX
は、強制終了するプロセスグループのグループ番号です。次の方法で確認できます。
$ ps x -o "%p %r %c"
PID PGID COMMAND
2416 1272 gnome-keyring-d
2427 2427 gnome-session
2459 2427 lightdm-session <defunct>
2467 2467 ssh-agent
2470 2427 dbus-launch
2471 2471 dbus-daemon
2484 2427 gnome-settings-
2489 2471 gvfsd
2491 2471 gvfs-Fuse-daemo
2499 2427 compiz
2502 2471 gconfd-2
2508 2427 syndaemon
2513 2512 pulseaudio
2517 2512 gconf-helper
2519 2471 gvfsd-metadata
プロセスグループIDの詳細については、man setpgid
:
DESCRIPTION
All of these interfaces are available on Linux, and are used for get‐
ting and setting the process group ID (PGID) of a process. The pre‐
ferred, POSIX.1-specified ways of doing this are: getpgrp(void), for
retrieving the calling process's PGID; and setpgid(), for setting a
process's PGID.
setpgid() sets the PGID of the process specified by pid to pgid. If
pid is zero, then the process ID of the calling process is used. If
pgid is zero, then the PGID of the process specified by pid is made the
same as its process ID. If setpgid() is used to move a process from
one process group to another (as is done by some shells when creating
pipelines), both process groups must be part of the same session (see
setsid(2) and credentials(7)). In this case, the pgid specifies an
existing process group to be joined and the session ID of that group
must match the session ID of the joining process.
親がPIDを処理していることがわかっている場合は、pkill
を使用してこれを実行できます。
$ pkill -TERM -P 27888
ここで、PPIDは27888です。
pkill manからの抜粋
-P, --parent ppid,...
Only match processes whose parent process ID is listed.
これはおそらく次の質問なので、Bashスクリプトで上部の$$
を使用してスクリプトのPIDを確認できます。
次のスクリプトがあるとします。
$ more somescript.bash
#!/bin/bash
echo "top: $$"
sleep 5
echo "bottom: $$"
今私はそれをバックグラウンドで実行します:
$ ./somescript.bash &
[2] 28007
top: 28007
pgrep
で確認すると、適切なPIDがあることがわかります。
$ pgrep somescript.bash
28007
$ bottom: 28007
[2]+ Done ./somescript.bash
このps
コマンドを使用すると、代わりに使用して強制終了できるプロセスPGIDを見つけることができます。
このスクリプトを使用して、killies.bash
:
$ more killies.bash
#!/bin/bash
sleep 1000 &
sleep 1000 &
sleep 1000 &
sleep 100
次のように実行します。
$ killies.bash &
チェックインする:
$ ps x -o "%p %r %c"
PID PGID COMMAND
28367 28367 killies.bash
28368 28367 sleep
28369 28367 sleep
28370 28367 sleep
28371 28367 sleep
次に、PGIDを強制終了します。
$ pkill -TERM -g 28367
[1]+ Terminated ./killies.bash
このSO Q&Aをご覧ください。
これを行う最良の方法は、アプリケーションの起動と停止にsystemd
(またはcgroupsを使用した別の方法)を使用することです。プロセスはプロセスグループを離れることができますが、(少なくともroot権限がなければ)cgroupを離れることはできません。したがって、systemd
は新しいプロセス用の新しいcgroupを作成し、後で単にcgroup内のすべてを強制終了します。
${PROC_CMD} &
pid=$!
kids=$(grep -l "PPid.*$$" /proc/*/status | grep -o "[0-9]*"
for kid in $(cat /proc/$pid/task/*/children); do
kids="$kid $kids $(cat /proc/$kid/task/*/children)"
done
printf '%s ' $kids)
kill $kids
これは、最初の行でバックグラウンドになっている${PROC_CMD},
のすべての子を殺し、その$pid
を次の行でキャプチャします。
cat /proc/$pid/task/*/children
上記は、子プロセスをリストするだけです。
プロセスエスケープ可能その$PPID.
を覚えておくことが重要です。例えば:
echo $( grep "Pid" /proc/self/status & )
Pid: 349 PPid: 1 TracerPid: 0