it-swarm.com.de

Wie kann man einen Kindprozess sterben lassen, nachdem die Eltern gegangen sind?

Angenommen, ich habe einen Prozess, der genau einen untergeordneten Prozess erzeugt. Wenn der übergeordnete Prozess aus irgendeinem Grund beendet wird (normalerweise oder ungewöhnlich, durch Töten, Bestätigen von Fehlern oder irgendetwas anderem), möchte ich, dass der untergeordnete Prozess stirbt. Wie mache ich das richtig?


Einige ähnliche Fragen zum Stackoverflow:


Einige ähnliche Fragen zum Stackoverflow für Windows :

199
Paweł Hajdan

Ein Kind kann den Kernel bitten, SIGHUP (oder ein anderes Signal) zu liefern, wenn ein Elternteil stirbt, indem es die Option PR_SET_PDEATHSIG In prctl() syscall wie folgt angibt:

prctl(PR_SET_PDEATHSIG, SIGHUP);

Siehe man 2 prctl Für Details.

Bearbeiten: Dies ist nur Linux

174
qrdl

Ich versuche, das gleiche Problem zu lösen, und da mein Programm unter OS X ausgeführt werden muss, hat die reine Linux-Lösung bei mir nicht funktioniert.

Ich bin zu demselben Schluss gekommen wie die anderen Personen auf dieser Seite - es gibt keine POSIX-kompatible Möglichkeit, ein Kind zu benachrichtigen, wenn ein Elternteil stirbt. Also habe ich die nächstbeste Sache geklärt - die Kinderumfrage.

Wenn ein übergeordneter Prozess (aus irgendeinem Grund) stirbt, wird der übergeordnete Prozess des Kindes zu Prozess 1. Wenn das Kind nur regelmäßig abruft, kann es überprüfen, ob der übergeordnete Prozess 1 ist. Wenn dies der Fall ist, sollte das Kind beenden.

Das ist nicht so toll, aber es funktioniert und es ist einfacher als die TCP Socket/Lockfile-Polling-Lösungen, die an anderer Stelle auf dieser Seite vorgeschlagen werden.

66
Schof

Ich habe dies in der Vergangenheit erreicht, indem ich den "ursprünglichen" Code im "untergeordneten" und den "hervorgebrachten" Code im "übergeordneten" Code ausgeführt habe (das heißt: Sie kehren den üblichen Sinn des Tests nach fork() um. ). Dann fange SIGCHLD im "gespawnten" Code ein ...

Ist in deinem Fall vielleicht nicht möglich, aber süß, wenn es funktioniert.

34
dmckee

Wenn Sie den untergeordneten Prozess nicht ändern können, können Sie Folgendes versuchen:

int pipes[2];
pipe(pipes)
if (fork() == 0) {
    close(pipes[1]); /* Close the writer end in the child*/
    dup2(0, pipes[0]); /* Use reader end as stdin */
    exec("sh -c 'set -o monitor; child_process & read dummy; kill %1'")
}

close(pipes[0]); /* Close the reader end in the parent */

Dadurch wird das untergeordnete Element in einem Shell-Prozess mit aktivierter Jobsteuerung ausgeführt. Der untergeordnete Prozess wird im Hintergrund erzeugt. Die Shell wartet auf eine neue Zeile (oder ein EOF) und tötet dann das Kind.

Wenn der Elternteil stirbt - egal aus welchem ​​Grund -, schließt er das Ende der Leitung. Die untergeordnete Shell erhält eine EOF vom Lesevorgang und bricht den untergeordneten Prozess im Hintergrund ab.

29
Phil Rutschman

Der Vollständigkeit halber. Unter macOS können Sie kqueue verwenden:

void noteProcDeath(
    CFFileDescriptorRef fdref, 
    CFOptionFlags callBackTypes, 
    void* info) 
{
    // LOG_DEBUG(@"noteProcDeath... ");

    struct kevent kev;
    int fd = CFFileDescriptorGetNativeDescriptor(fdref);
    kevent(fd, NULL, 0, &kev, 1, NULL);
    // take action on death of process here
    unsigned int dead_pid = (unsigned int)kev.ident;

    CFFileDescriptorInvalidate(fdref);
    CFRelease(fdref); // the CFFileDescriptorRef is no longer of any use in this example

    int our_pid = getpid();
    // when our parent dies we die as well.. 
    LOG_INFO(@"exit! parent process (pid %u) died. no need for us (pid %i) to stick around", dead_pid, our_pid);
    exit(EXIT_SUCCESS);
}


void suicide_if_we_become_a_zombie(int parent_pid) {
    // int parent_pid = getppid();
    // int our_pid = getpid();
    // LOG_ERROR(@"suicide_if_we_become_a_zombie(). parent process (pid %u) that we monitor. our pid %i", parent_pid, our_pid);

    int fd = kqueue();
    struct kevent kev;
    EV_SET(&kev, parent_pid, EVFILT_PROC, EV_ADD|EV_ENABLE, NOTE_EXIT, 0, NULL);
    kevent(fd, &kev, 1, NULL, 0, NULL);
    CFFileDescriptorRef fdref = CFFileDescriptorCreate(kCFAllocatorDefault, fd, true, noteProcDeath, NULL);
    CFFileDescriptorEnableCallBacks(fdref, kCFFileDescriptorReadCallBack);
    CFRunLoopSourceRef source = CFFileDescriptorCreateRunLoopSource(kCFAllocatorDefault, fdref, 0);
    CFRunLoopAddSource(CFRunLoopGetMain(), source, kCFRunLoopDefaultMode);
    CFRelease(source);
}
14
neoneye

Unter Linux können Sie ein übergeordnetes Sterbesignal im Kind installieren, z.

#include <sys/prctl.h> // prctl(), PR_SET_PDEATHSIG
#include <signal.h> // signals
#include <unistd.h> // fork()
#include <stdio.h>  // perror()

// ...

pid_t ppid_before_fork = getpid();
pid_t pid = fork();
if (pid == -1) { perror(0); exit(1); }
if (pid) {
    ; // continue parent execution
} else {
    int r = prctl(PR_SET_PDEATHSIG, SIGTERM);
    if (r == -1) { perror(0); exit(1); }
    // test in case the original parent exited just
    // before the prctl() call
    if (getppid() != ppid_before_fork)
        exit(1);
    // continue child execution ...

Beachten Sie, dass das Speichern der übergeordneten Prozess-ID vor dem Fork und das Testen im untergeordneten Prozess nach prctl() eine Racebedingung zwischen prctl() eliminiert und der Ausgang des Prozesses, der das Kind anrief.

Beachten Sie auch, dass das Sterbesignal der Eltern des Kindes bei neu erstellten eigenen Kindern gelöscht wird. Es ist nicht von einer execve() betroffen.

Dieser Test kann vereinfacht werden, wenn wir sicher sind, dass der Systemprozess, der für die Übernahme aller Waisen zuständig ist, PID 1 hat:

pid_t pid = fork();
if (pid == -1) { perror(0); exit(1); }
if (pid) {
    ; // continue parent execution
} else {
    int r = prctl(PR_SET_PDEATHSIG, SIGTERM);
    if (r == -1) { perror(0); exit(1); }
    // test in case the original parent exited just
    // before the prctl() call
    if (getppid() == 1)
        exit(1);
    // continue child execution ...

Sich darauf zu verlassen, dass der Systemprozess init ist und PID 1 hat, ist jedoch nicht portierbar. POSIX.1-2008 spezifiziert :

Die übergeordnete Prozess-ID aller vorhandenen untergeordneten Prozesse und Zombie-Prozesse des aufrufenden Prozesses wird auf die Prozess-ID eines implementierungsdefinierten Systemprozesses festgelegt. Das heißt, diese Prozesse sollen von einem speziellen Systemprozess vererbt werden.

Traditionell ist der Systemprozess, bei dem alle Waisenkinder übernommen werden, PID 1, d. H. Init - der Vorläufer aller Prozesse.

Auf modernen Systemen wie Linux oder FreeBSD könnte ein anderer Prozess diese Rolle spielen. Unter Linux kann ein Prozess beispielsweise prctl(PR_SET_CHILD_SUBREAPER, 1) aufrufen, um sich als Systemprozess zu etablieren, der alle Waisenkinder seiner Nachkommen erbt (vgl. Ein Beispiel = auf Fedora 25).

12
maxschlepzig

Hat der untergeordnete Prozess eine Pipe zum/vom übergeordneten Prozess? In diesem Fall würden Sie beim Schreiben eine SIEGELRIPPE erhalten oder beim Lesen EOF - diese Bedingungen könnten erkannt werden.

11
MarkR

Inspiriert von einer anderen Antwort hier, kam ich auf die folgende All-POSIX-Lösung. Die allgemeine Idee besteht darin, einen Zwischenprozess zwischen dem Elternteil und dem Kind zu erstellen, der einen Zweck hat: Beachten Sie, wann das Elternteil stirbt, und töten Sie das Kind explizit.

Diese Art von Lösung ist nützlich, wenn der Code im untergeordneten Element nicht geändert werden kann.

int p[2];
pipe(p);
pid_t child = fork();
if (child == 0) {
    close(p[1]); // close write end of pipe
    setpgid(0, 0); // prevent ^C in parent from stopping this process
    child = fork();
    if (child == 0) {
        close(p[0]); // close read end of pipe (don't need it here)
        exec(...child process here...);
        exit(1);
    }
    read(p[0], 1); // returns when parent exits for any reason
    kill(child, 9);
    exit(1);
}

Bei dieser Methode gibt es zwei kleine Einschränkungen:

  • Wenn Sie den Zwischenprozess absichtlich beenden, wird das Kind nicht getötet, wenn das Elternteil stirbt.
  • Wenn das untergeordnete Element vor dem übergeordneten Element beendet wird, versucht der Zwischenprozess, die ursprüngliche untergeordnete PID zu beenden, die nun auf einen anderen Prozess verweisen kann. (Dies könnte mit mehr Code im Zwischenprozess behoben werden.)

Abgesehen davon ist der eigentliche Code, den ich verwende, in Python. Hier ist es der Vollständigkeit halber:

def run(*args):
    (r, w) = os.pipe()
    child = os.fork()
    if child == 0:
        os.close(w)
        os.setpgid(0, 0)
        child = os.fork()
        if child == 0:
            os.close(r)
            os.execl(args[0], *args)
            os._exit(1)
        os.read(r, 1)
        os.kill(child, 9)
        os._exit(1)
    os.close(r)
10
Greg Hewgill

Ich glaube nicht, dass es möglich ist zu garantieren, dass nur Standard-POSIX-Aufrufe verwendet werden. Wie im richtigen Leben hat ein Kind, wenn es einmal geboren wurde, ein eigenes Leben.

Es ist ist möglich, dass der übergeordnete Prozess die meisten möglichen Beendigungsereignisse abfängt und versucht, den untergeordneten Prozess an diesem Punkt zu beenden, aber es gibt immer einige, die nicht abgefangen werden können.

Beispielsweise kann kein Prozess ein SIGKILL abfangen. Wenn der Kernel dieses Signal verarbeitet, bricht er den angegebenen Prozess ab, ohne dass eine Benachrichtigung an diesen Prozess erfolgt.

Um die Analogie zu erweitern - die einzige andere Standardmethode besteht darin, dass das Kind Selbstmord begeht, wenn es feststellt, dass es keine Eltern mehr hat.

Es gibt eine Möglichkeit, dies nur unter Linux mit prctl(2) zu tun - siehe andere Antworten.

7
Alnitak

Wie bereits erwähnt, ist es nicht übertragbar, sich darauf zu verlassen, dass die übergeordnete PID 1 wird, wenn die übergeordnete PID beendet wird. Anstatt auf eine bestimmte übergeordnete Prozess-ID zu warten, warten Sie einfach, bis sich die ID ändert:

pit_t pid = getpid();
switch (fork())
{
    case -1:
    {
        abort(); /* or whatever... */
    }
    default:
    {
        /* parent */
        exit(0);
    }
    case 0:
    {
        /* child */
        /* ... */
    }
}

/* Wait for parent to exit */
while (getppid() != pid)
    ;

Fügen Sie nach Wunsch einen Mikroschlaf hinzu, wenn Sie nicht mit voller Geschwindigkeit abrufen möchten.

Diese Option erscheint mir einfacher als die Verwendung einer Pipe oder die Verwendung von Signalen.

6
user2168915

Installieren Sie einen Trap-Handler, um SIGINT zu fangen, der den Prozess Ihres Kindes abbricht, wenn er noch lebt, obwohl andere Poster korrekt sind, dass er SIGKILL nicht fängt.

Öffnen Sie eine .lock-Datei mit exklusivem Zugriff und lassen Sie die untergeordnete Umfrage darauf versuchen, sie zu öffnen. Wenn das Öffnen erfolgreich ist, sollte der untergeordnete Prozess beendet werden

4
Paul Betts

Diese Lösung hat bei mir funktioniert:

  • Übergeben Sie stdin pipe an child - Sie müssen keine Daten in den Stream schreiben.
  • Kind liest auf unbestimmte Zeit von stdin bis EOF. Ein EOF signalisiert, dass der Elternteil gegangen ist.
  • Dies ist eine kinderleichte und tragbare Methode, um festzustellen, wann die Eltern gegangen sind. Auch wenn Eltern abstürzt, wird OS die Pipe schließen.

Dies war für einen Arbeitsprozess gedacht, dessen Existenz nur Sinn machte, wenn der Elternteil am Leben war.

4
joonas.fi

Ich denke, ein schneller und schmutziger Weg ist es, eine Pfeife zwischen Kind und Eltern zu schaffen. Beim Verlassen des Elternteils erhalten Kinder eine SIEGELRUNDE.

3
Yefei

Einige Poster haben bereits Pipes und kqueue erwähnt. Tatsächlich können Sie auch ein Paar verbundener nix-Domain-Sockets durch den socketpair()-Aufruf erstellen. Der Socket-Typ sollte SOCK_STREAM Sein.

Nehmen wir an, Sie haben die beiden Socket-Dateideskriptoren fd1, fd2. Nun fork(), um den untergeordneten Prozess zu erstellen, der die fds erbt. Im Elternteil schließen Sie fd2 und im Kind schließen Sie fd1. Jetzt kann jeder Prozess das verbleibende offene fd für das Ereignis POLLIN an seinem eigenen Ende poll(). Solange jede Seite während der normalen Lebensdauer nicht explizit ihre fd close() anzeigt, können Sie ziemlich sicher sein, dass ein POLLHUP -Flag die Beendigung der anderen Seite anzeigen sollte (egal ob sauber oder nicht). Nach Benachrichtigung über dieses Ereignis kann das Kind entscheiden, was zu tun ist (z. B. zu sterben).

#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <poll.h>
#include <stdio.h>

int main(int argc, char ** argv)
{
    int sv[2];        /* sv[0] for parent, sv[1] for child */
    socketpair(AF_UNIX, SOCK_STREAM, 0, sv);

    pid_t pid = fork();

    if ( pid > 0 ) {  /* parent */
        close(sv[1]);
        fprintf(stderr, "parent: pid = %d\n", getpid());
        sleep(100);
        exit(0);

    } else {          /* child */
        close(sv[0]);
        fprintf(stderr, "child: pid = %d\n", getpid());

        struct pollfd mon;
        mon.fd = sv[1];
        mon.events = POLLIN;

        poll(&mon, 1, -1);
        if ( mon.revents & POLLHUP )
            fprintf(stderr, "child: parent hung up\n");
        exit(0);
    }
}

Sie können versuchen, den obigen Proof-of-Concept-Code zu kompilieren und ihn in einem Terminal wie ./a.out & Ausführen. Sie haben ungefähr 100 Sekunden Zeit, um mit dem Töten der übergeordneten PID durch verschiedene Signale zu experimentieren, oder sie wird einfach beendet. In beiden Fällen sollte die Meldung "Kind: Elternteil aufgelegt" angezeigt werden.

Verglichen mit der Methode mit dem SIGPIPE -Handler muss für diese Methode der write() -Aufruf nicht ausgeführt werden.

Diese Methode ist auch symmetrisch , d. H. Die Prozesse können denselben Kanal verwenden, um die Existenz des jeweils anderen zu überwachen.

Diese Lösung ruft nur die POSIX-Funktionen auf. Ich habe es unter Linux und FreeBSD versucht. Ich denke, es sollte unter anderen Unixen funktionieren, aber ich habe es nicht wirklich getestet.

Siehe auch:

  • unix(7) von Linux-Manpages, unix(4) für FreeBSD, poll(2), socketpair(2), socket(7) unter Linux.
3
Cong Ma

Für den Fall, dass es für andere relevant ist, wenn ich JVM-Instanzen in gegabelten untergeordneten Prozessen von C++ aus spawne, bestand die einzige Möglichkeit, die JVM-Instanzen ordnungsgemäß zu beenden, nachdem der übergeordnete Prozess abgeschlossen wurde, darin, Folgendes zu tun. Hoffentlich kann jemand in den Kommentaren Feedback geben, wenn dies nicht der beste Weg ist, dies zu tun.

1) Rufen Sie prctl(PR_SET_PDEATHSIG, SIGHUP) für den Forked Child-Prozess auf, wie vorgeschlagen, bevor Sie die Java -Anwendung über execv und starten

2) Fügen Sie der Anwendung Java einen Shutdown-Hook hinzu, der abruft, bis die übergeordnete PID gleich 1 ist, und führen Sie dann eine harte Runtime.getRuntime().halt(0) aus. Zum Abrufen wird eine separate Shell gestartet, die den Befehl ps ausführt. (Siehe: Wie finde ich meine PID in Java oder JRuby unter Linux? ).

EDIT 130118:

Es scheint, dass dies keine robuste Lösung war. Ich habe immer noch ein wenig Mühe, die Feinheiten der Vorgänge zu verstehen, aber es kam manchmal vor, dass verwaiste JVM-Prozesse auftreten, wenn diese Anwendungen in Bildschirm-/SSH-Sitzungen ausgeführt werden.

Anstatt die PPID in der App Java abzufragen, ließ ich einfach den Shutdown-Hook bereinigen, gefolgt von einem harten Stopp wie oben. Dann habe ich dafür gesorgt, dass waitpid in der übergeordneten C++ - App auf dem untergeordneten Prozess aufgerufen wurde, als es Zeit war, alles zu beenden. Dies scheint eine robustere Lösung zu sein, da der untergeordnete Prozess sicherstellt, dass er beendet wird, während der übergeordnete Prozess vorhandene Verweise verwendet, um sicherzustellen, dass seine untergeordneten Prozesse beendet werden. Vergleichen Sie dies mit der vorherigen Lösung, bei der der übergeordnete Prozess beendet wurde, wann immer er wollte, und die Kinder versuchten, herauszufinden, ob sie vor dem Beenden verwaist waren.

1
jasterm007

Unter POSIX werden die Funktionen exit(), _exit() und _Exit() wie folgt definiert:

  • Wenn der Prozess ein steuernder Prozess ist, soll das Signal SIGHUP an jeden Prozess in der Vordergrundprozessgruppe des steuernden Endgeräts gesendet werden, das zu dem aufrufenden Prozess gehört.

Wenn Sie also festlegen, dass der übergeordnete Prozess ein Steuerungsprozess für seine Prozessgruppe ist, sollte das untergeordnete Element ein SIGHUP-Signal erhalten, wenn der übergeordnete Prozess beendet wird. Ich bin mir nicht ganz sicher, ob das passiert, wenn der Elternteil abstürzt, aber ich denke, das tut es. In den Fällen, in denen kein Absturz aufgetreten ist, sollte dies problemlos funktionieren.

Beachten Sie, dass Sie möglicherweise viel Kleingedrucktes lesen müssen - einschließlich des Abschnitts Basisdefinitionen (Definitionen) sowie der Systemdienstinformationen für exit() und setsid() und setpgrp() - um ein vollständiges Bild zu erhalten. (Also würde ich!)

1

Wenn Sie beispielsweise ein Signal an die PID 0 senden

kill(0, 2); /* SIGINT */

dieses Signal wird an die gesamte Prozessgruppe gesendet, wodurch das Kind effektiv getötet wird.

Sie können es leicht mit etwas testen:

(cat && kill 0) | python

Wenn Sie dann ^ D drücken, wird der Text "Terminated" als Hinweis darauf, dass der Python Interpreter tatsächlich getötet wurde, anstatt nur aufgrund des Schließens von stdin beendet zu werden.

1

In der Vergangenheit hat das Prozesssystem unter UNIX v7 verwaiste Prozesse erkannt, indem es die übergeordnete ID eines Prozesses überprüfte. Historisch gesehen ist der Systemprozess init(8) nur aus einem Grund ein spezieller Prozess: Er kann nicht sterben. Es kann nicht sterben, da der Kernel-Algorithmus für die Zuweisung einer neuen übergeordneten Prozess-ID von dieser Tatsache abhängt. Wenn ein Prozess seinen exit(2) -Aufruf ausführt (durch einen Prozesssystemaufruf oder durch eine externe Task, die ihm ein Signal oder dergleichen sendet), weist der Kernel allen untergeordneten Elementen dieses Prozesses die ID des init-Prozesses als ihre ID zu übergeordnete Prozess-ID Dies führt zu dem einfachsten Test und der einfachsten Methode, um festzustellen, ob ein Prozess verwaist ist. Überprüfen Sie einfach das Ergebnis des Systemaufrufs getppid(2), und wenn es sich um die Prozess-ID des Prozesses init(2) handelt, hat der Prozess vor dem Systemaufruf Orphan erhalten.

Aus diesem Ansatz ergeben sich zwei Probleme, die zu Problemen führen können:

  • erstens haben wir die Möglichkeit, den init -Prozess in einen beliebigen Benutzerprozess zu ändern. Wie können wir also sicherstellen, dass der init -Prozess immer allen Orphan-Prozessen übergeordnet ist? Nun, im exit Systemaufrufcode wird explizit geprüft, ob der Prozess, der den Aufruf ausführt, der Init-Prozess ist (der Prozess mit einer PID von 1) und in diesem Fall die Kernel-Panik (das sollte der Fall sein) nicht mehr in der Lage sein, die Prozesshierarchie beizubehalten), so ist es dem init-Prozess nicht gestattet, einen exit(2) -Aufruf auszuführen.
  • zweitens gibt es eine Rennbedingung in dem oben gezeigten Basistest. In der Vergangenheit wird angenommen, dass die ID des Init-Prozesses 1 Ist. Dies wird jedoch durch den POSIX-Ansatz nicht garantiert. Dieser besagt (wie in einer anderen Antwort angegeben), dass nur die Prozess-ID eines Systems für diesen Zweck reserviert ist. Nahezu keine Posix-Implementierung tut dies, und Sie können in ursprünglichen, von Unix abgeleiteten Systemen davon ausgehen, dass 1 Als Antwort auf den Systemaufruf getppid(2) ausreicht, um anzunehmen, dass der Prozess verwaist ist. Eine andere Möglichkeit, dies zu überprüfen, besteht darin, unmittelbar nach der Verzweigung eine getppid(2) zu erstellen und diesen Wert mit dem Ergebnis eines neuen Aufrufs zu vergleichen. Dies funktioniert einfach nicht in allen Fällen, da beide Aufrufe nicht zusammen atomar sind und der übergeordnete Prozess nach dem fork(2) und vor dem ersten getppid(2) Systemaufruf abstürzen kann. Der Prozess parent id only changes once, when its parent does an Beendet (2) call, so this should be enough to check if the Getppid (2) result changed between calls to see that parent process has exit. This test is not valid for the actual children of the init process, because they are always children of Init (8) `, aber Sie können davon ausgehen, dass diese Prozesse auch keine übergeordneten Prozesse haben (außer Sie in einem System den Init-Prozess ersetzen)
0
Luis Colorado

Obwohl 7 Jahre vergangen sind, bin ich gerade auf dieses Problem gestoßen, als ich eine SpringBoot-Anwendung ausführe, die den webpack-dev-server während der Entwicklung starten muss und die beendet werden muss, wenn der Back-End-Prozess stoppt.

Ich versuche, Runtime.getRuntime().addShutdownHook zu verwenden, aber es funktionierte unter Windows 10, jedoch nicht unter Windows 7.

Ich habe es geändert, um einen dedizierten Thread zu verwenden, der darauf wartet, dass der Prozess beendet wird, oder auf InterruptedException, der auf beiden Windows-Versionen korrekt zu funktionieren scheint.

private void startWebpackDevServer() {
    String cmd = isWindows() ? "cmd /c gradlew webPackStart" : "gradlew webPackStart";
    logger.info("webpack dev-server " + cmd);

    Thread thread = new Thread(() -> {

        ProcessBuilder pb = new ProcessBuilder(cmd.split(" "));
        pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
        pb.redirectError(ProcessBuilder.Redirect.INHERIT);
        pb.directory(new File("."));

        Process process = null;
        try {
            // Start the node process
            process = pb.start();

            // Wait for the node process to quit (blocking)
            process.waitFor();

            // Ensure the node process is killed
            process.destroyForcibly();
            System.setProperty(WEBPACK_SERVER_PROPERTY, "true");
        } catch (InterruptedException | IOException e) {
            // Ensure the node process is killed.
            // InterruptedException is thrown when the main process exit.
            logger.info("killing webpack dev-server", e);
            if (process != null) {
                process.destroyForcibly();
            }
        }

    });

    thread.start();
}
0
Ido Ran

Ich habe 2 Lösungen gefunden, beide nicht perfekt.

1. Töte alle Kinder durch Töten (-pid), wenn ein SIGTERM-Signal empfangen wird.
Natürlich kann diese Lösung nicht mit "kill -9" umgehen, aber sie funktioniert in den meisten Fällen und ist sehr einfach, da nicht alle untergeordneten Prozesse gespeichert werden müssen.


    var childProc = require('child_process').spawn('tail', ['-f', '/dev/null'], {stdio:'ignore'});

    var counter=0;
    setInterval(function(){
      console.log('c  '+(++counter));
    },1000);

    if (process.platform.slice(0,3) != 'win') {
      function killMeAndChildren() {
        /*
        * On Linux/Unix(Include Mac OS X), kill (-pid) will kill process group, usually
        * the process itself and children.
        * On Windows, an JOB object has been applied to current process and children,
        * so all children will be terminated if current process dies by anyway.
        */
        console.log('kill process group');
        process.kill(-process.pid, 'SIGKILL');
      }

      /*
      * When you use "kill pid_of_this_process", this callback will be called
      */
      process.on('SIGTERM', function(err){
        console.log('SIGTERM');
        killMeAndChildren();
      });
    }

Auf die gleiche Weise können Sie den Exit-Handler wie oben beschrieben installieren, wenn Sie process.exit irgendwo aufrufen. Hinweis: Strg + C und plötzlicher Absturz wurden vom Betriebssystem automatisch verarbeitet, um die Prozessgruppe zu beenden.

2.Verwenden Sie chjj/pty.js , um Ihren Prozess mit angeschlossenem Steuerungsterminal zu erzeugen.
Wenn Sie den aktuellen Prozess mit -9 beenden, werden auch alle untergeordneten Prozesse automatisch beendet (vom Betriebssystem?). Ich vermute, dass, weil der aktuelle Prozess eine andere Seite des Terminals hält, der untergeordnete Prozess SIGPIPE erhält, wenn der aktuelle Prozess stirbt.


    var pty = require('pty.js');

    //var term =
    pty.spawn('any_child_process', [/*any arguments*/], {
      name: 'xterm-color',
      cols: 80,
      rows: 30,
      cwd: process.cwd(),
      env: process.env
    });
    /*optionally you can install data handler
    term.on('data', function(data) {
      process.stdout.write(data);
    });
    term.write(.....);
    */

0
osexp2003

Eine andere Möglichkeit, dies Linux-spezifisch zu tun, besteht darin, das übergeordnete Element in einem neuen PID-Namespace zu erstellen. Es wird dann PID 1 in diesem Namespace sein, und wenn es ihn verlässt, werden alle seine Kinder sofort mit SIGKILL getötet.

Um einen neuen PID-Namespace zu erstellen, muss leider CAP_SYS_ADMIN. Diese Methode ist jedoch sehr effektiv und erfordert keine wirklichen Änderungen für die Eltern oder die Kinder nach dem ersten Start der Eltern.

Siehe clone (2) , pid_namespaces (7) und nshare (2) .

0
Omnifarious

Ich habe es geschafft, eine portable, nicht abfragende Lösung mit drei Prozessen zu erstellen, indem ich die Terminalsteuerung und Sitzungen missbraucht habe. Das ist geistige Masturbation, funktioniert aber.

Der Trick ist:

  • prozess A wird gestartet
  • prozess A erzeugt eine Pipe P (und liest nie aus ihr)
  • prozess A geht in Prozess B über
  • prozess B erstellt eine neue Sitzung
  • prozess B weist ein virtuelles Terminal für diese neue Sitzung zu
  • prozess B installiert den SIGCHLD-Handler so, dass er stirbt, wenn das Kind beendet wird
  • prozess B legt einen SIGPIPE-Handler fest
  • prozess B geht in Prozess C über
  • prozess C tut, was es braucht (z. B. exec () s die nicht modifizierte Binärdatei oder führt eine beliebige Logik aus)
  • prozess B schreibt in Pipe (und blockiert auf diese Weise)
  • process A wait () ist auf process B und wird beendet, wenn es stirbt

Dieser Weg:

  • wenn Prozess A stirbt: Prozess B erhält ein SIEGEL und stirbt
  • wenn Prozess B stirbt: Prozess A's wait () kehrt zurück und stirbt, erhält Prozess C einen SIGHUP (wenn der Sitzungsleiter einer Sitzung mit angeschlossenem Terminal stirbt, erhalten alle Prozesse in der Vordergrundprozessgruppe einen SIGHUP).
  • wenn Prozess C stirbt: Prozess B erhält eine SIGCHLD und stirbt, stirbt Prozess A

Mängel:

  • prozess C kann SIGHUP nicht verarbeiten
  • prozess C wird in einer anderen Sitzung ausgeführt
  • prozess C kann die Sitzungs-/Prozessgruppen-API nicht verwenden, da dies das spröde Setup beeinträchtigt
  • es ist nicht die beste Idee, für jeden dieser Vorgänge ein Terminal zu erstellen
0
Marek Dopiera

Wenn ein Elternteil stirbt, ändert sich die PPID der Waisenkinder auf 1 - Sie müssen nur Ihre eigene PPID überprüfen. In gewisser Weise handelt es sich um eine Abfrage, die oben erwähnt wurde. Hier ist das Muschelstück dafür:

check_parent () {
      parent=`ps -f|awk '$2=='$PID'{print $3 }'`
      echo "parent:$parent"
      let parent=$parent+0
      if [[ $parent -eq 1 ]]; then
        echo "parent is dead, exiting"
        exit;
      fi
}


PID=$$
cnt=0
while [[ 1 = 1 ]]; do
  check_parent
  ... something
done
0
alex K