it-swarm.com.de

Töte alle Nachkommenprozesse

Ich schreibe eine Bewerbung. Es hat die Fähigkeit, verschiedene externe Prozesse zu erzeugen. Wenn die Anwendung geschlossen wird, möchte ich, dass alle Prozesse, die sie erzeugt hat, beendet werden.

Klingt einfach genug, oder? Schauen Sie sich meine PID an und gehen Sie rekursiv über den Prozessbaum, wobei Sie alles in Sichtweite von unten nach oben töten.

Nur dass dies nicht funktioniert. In einem bestimmten Fall spawne ich foo, aber foo spawnt nur bar und wird dann sofort beendet, wobei bar ausgeführt wird. Es gibt jetzt kein Datensatz der Tatsache, dass bar einmal Teil des Prozessbaums der Anwendung war. Und daher kann die Anwendung nicht wissen, dass sie bar töten soll.

Ich bin mir ziemlich sicher, dass ich nicht die erste Person auf der Erde sein kann, die dies versucht. Was ist die Standardlösung? Ich denke wirklich, ich suche nach einer Möglichkeit, einen Prozess so zu "markieren", dass jeder Prozess, den er erzeugt, bedingungslos dasselbe Tag erbt.

(Bisher kann ich die Anwendung nur als einen anderen Benutzer ausführen. Auf diese Weise können Sie alle Prozesse, die diesem Benutzer gehören, wahllos beenden. Dies hat jedoch alle möglichen Probleme mit Zugriffsberechtigungen ...)

62

Aktualisieren

Dies ist einer von denen, bei denen ich die Frage eindeutig genauer hätte lesen sollen (obwohl dies anscheinend bei den meisten Antworten auf diese Frage der Fall ist). Ich habe die ursprüngliche Antwort intakt gelassen, weil sie einige gute Informationen enthält, obwohl sie den Punkt der Frage eindeutig verfehlt.

Verwenden der SID

Ich denke, der allgemeinste und robusteste Ansatz (zumindest für Linux) ist die Verwendung von SID (Session ID) anstelle von PPID oder PGID. Es ist viel weniger wahrscheinlich, dass dies durch untergeordnete Prozesse geändert wird, und im Fall eines Shell-Skripts kann der Befehl setsid zum Starten einer neuen Sitzung verwendet werden. Außerhalb der Shell kann der Systemaufruf setuid verwendet werden.

Für eine Shell, die ein Sitzungsleiter ist, können Sie alle anderen Prozesse in der Sitzung beenden, indem Sie Folgendes tun (die Shell beendet sich nicht selbst):

kill $(ps -s $$ -o pid=)

Hinweis: Das nachfolgende Gleichheitszeichen-Argument pid= Entfernt die Spaltenüberschrift PID.

Andernfalls scheint bei Verwendung von Systemaufrufen der Aufruf von getsid für jeden Prozess der einzige Weg zu sein.

Verwenden eines PID-Namensraums

Dies ist der robusteste Ansatz. Die Nachteile sind jedoch, dass es sich nur um Linux handelt und Root-Berechtigungen erforderlich sind. Auch die Shell-Tools (falls verwendet) sind sehr neu und nicht weit verbreitet.

Eine ausführlichere Beschreibung der PID-Namespaces finden Sie in dieser Frage - Zuverlässige Methode zum Unterwerfen untergeordneter Prozesse mit `nsenter:` . Der grundlegende Ansatz hierbei ist, dass Sie einen neuen PID-Namespace erstellen können, indem Sie das Flag CLONE_NEWPID Mit dem Systemaufruf clone (oder über den Befehl unshare) verwenden.

Wenn ein Prozess in einem PID-Namespace verwaist ist (dh wenn der übergeordnete Prozess beendet ist), wird er erneut dem PID-Namespace-Prozess der obersten Ebene und nicht dem init übergeordnet. Dies bedeutet, dass Sie immer alle Nachkommen des Prozesses der obersten Ebene identifizieren können, indem Sie den Prozessbaum durchlaufen. Im Fall eines Shell-Skripts würde der folgende PPID-Ansatz dann zuverlässig alle Nachkommen töten.

Weitere Informationen zu PID-Namespaces:

Ursprüngliche Antwort

Kinderprozesse töten

Der einfache Weg, dies in einem Shell-Skript zu tun, vorausgesetzt, pkill ist verfügbar:

pkill -P $$

Dadurch werden alle untergeordneten Elemente des aktuell angegebenen Prozesses beendet ($$ Wird auf die PID der aktuellen Shell erweitert).

Wenn pkill nicht verfügbar ist, ist eine POSIX-kompatible Methode:

kill $(ps -o pid= --ppid $$)

Alle absteigenden Prozesse beenden

Eine andere Situation ist, dass Sie möglicherweise alle Nachkommen des aktuellen Shell-Prozesses sowie nur die direkten untergeordneten Elemente töten möchten. In diesem Fall können Sie die unten stehende rekursive Shell-Funktion verwenden, um alle untergeordneten PIDs aufzulisten, bevor Sie sie als Argumente zum Beenden übergeben:

list_descendants ()
{
  local children=$(ps -o pid= --ppid "$1")

  for pid in $children
  do
    list_descendants "$pid"
  done

  echo "$children"
}

kill $(list_descendants $$)

Doppelgabeln

Eine Sache, die Sie beachten sollten, um zu verhindern, dass das oben Genannte wie erwartet funktioniert, ist die doppelte fork() Technik. Dies wird häufig beim Dämonisieren eines Prozesses verwendet. Wie der Name schon sagt, wird der zu startende Prozess in der zweiten Verzweigung des ursprünglichen Prozesses ausgeführt. Sobald der Prozess gestartet ist, wird die erste Gabelung beendet, was bedeutet, dass der Prozess verwaist ist.

In diesem Fall wird es ein untergeordnetes Element des Prozesses init anstelle des ursprünglichen Prozesses, von dem aus es gestartet wurde. Es gibt keine zuverlässige Methode, um festzustellen, welcher Prozess der ursprüngliche übergeordnete Prozess war. Wenn dies der Fall ist, können Sie nicht erwarten, dass Sie ihn beenden können, ohne über eine andere Art der Identifizierung zu verfügen (z. B. eine PID-Datei). Wenn diese Technik verwendet wurde, sollten Sie jedoch nicht versuchen, den Prozess ohne guten Grund abzubrechen.

Weiterführende Literatur:

57
Graeme

Sie können verwenden:

kill -TERM -- -XXX

dabei ist XXX die Gruppennummer der Prozessgruppe, die Sie beenden möchten. Sie können dies überprüfen mit:

 $ 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

Weitere Informationen zur Prozessgruppen-ID finden Sie unter 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.
23
cuonglm

Wenn Sie die PID der übergeordneten Prozesse kennen, können Sie dies mit pkill tun.

Beispiel

$ pkill -TERM -P 27888

Wo die PPID 27888 ist.

Auszug aus pkill man

   -P, --parent ppid,...
          Only match processes whose parent process ID is listed.

Was ist meine PID in einem Skript?

Dies ist wahrscheinlich Ihre nächste Frage. Wenn Sie also in einem Bash-Skript die PID des Skripts mit $$ Oben ermitteln können.

Beispiel

Angenommen, ich habe dieses Skript:

$ more somescript.bash 
#!/bin/bash

echo "top: $$"
sleep 5
echo "bottom: $$"

Jetzt führe ich es im Hintergrund aus:

$ ./somescript.bash &
[2] 28007
top: 28007

Ein Blick mit pgrep zeigt, dass wir die richtige PID haben:

$ pgrep somescript.bash
28007
$ bottom: 28007

[2]+  Done                    ./somescript.bash

Verwenden eines Prozesses 'PGID

Wenn Sie diesen Befehl ps verwenden, können Sie eine Prozess-PGID ermitteln, die Sie stattdessen beenden können.

Verwenden Sie jetzt dieses Skript, killies.bash:

$ more killies.bash 
#!/bin/bash

sleep 1000 &
sleep 1000 &
sleep 1000 &

sleep 100

Wir führen es so:

$ killies.bash &

Einchecken:

$ 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

Jetzt töten wir die PGID:

$ pkill -TERM -g 28367
[1]+  Terminated              ./killies.bash

Zusätzliche Methoden

Wenn Sie sich das ansehen SO Q & A, finden Sie noch mehr Methoden, um das zu tun, was Sie wollen:

Verweise

11
slm

Der beste Weg, dies zu tun, besteht darin, systemd (oder einen anderen Weg unter Verwendung von cgroups) zum Starten und Stoppen der Anwendung zu verwenden. Ein Prozess kann seine Prozessgruppe verlassen, aber niemals (zumindest nicht ohne Root-Berechtigung) seine cgroup verlassen. Somit erstellt systemd eine neue cgroup für den neuen Prozess und beendet später einfach alles in der cgroup.

5
Hauke Laging
${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

Das tötet alle Kinder von ${PROC_CMD},, das in der ersten Zeile und seinem $pid im nächsten erfasst.

cat /proc/$pid/task/*/children

Oben werden nur die untergeordneten Prozesse aufgelistet.

Es ist wichtig, sich daran zu erinnern, dass ein Prozess kann entkommen sein $PPID. Zum Beispiel:

echo $( grep "Pid" /proc/self/status & )

Pid: 349 PPid: 1 TracerPid: 0
2
mikeserv