Wie lässt man den untergeordneten Prozess sterben, nachdem der übergeordnete Prozess beendet wurde?

StackOverflow https://stackoverflow.com/questions/284325

  •  08-07-2019
  •  | 
  •  

Frage

Angenommen, ich habe einen Prozess, der genau einen untergeordneten Prozess erzeugt.Wenn nun der übergeordnete Prozess aus irgendeinem Grund beendet wird (normalerweise oder ungewöhnlich, durch Kill, ^C, Assert-Fehler oder irgendetwas anderes), möchte ich, dass der untergeordnete Prozess stirbt.Wie macht man das richtig?


Eine ähnliche Frage zum Stackoverflow:


Eine ähnliche Frage zum Stackoverflow für Windows:

War es hilfreich?

Lösung

Kind kann Kernel fragen SIGHUP (oder ein anderes Signal) zu liefern, wenn Elternteil stirbt durch Option PR_SET_PDEATHSIG in prctl() syscall wie folgt spezifiziert:

prctl(PR_SET_PDEATHSIG, SIGHUP);

Siehe man 2 prctl für Details.

Edit: Dies ist Linux-only

Andere Tipps

Ich versuche, das gleiche Problem zu lösen, und da mein Programm unter OS X ausgeführt werden muss, hat die Linux-only Lösung nicht für mich arbeiten.

Ich kam zu dem gleichen Schluss wie die anderen Menschen auf dieser Seite - es gibt keine POSIX-kompatibel ist Weg, um ein Kind zu benachrichtigen, wenn ein Elternteil stirbt. So kludged ich die nächstbeste Sache up -. Das Kind Umfrage mit

Wenn ein Elternteil stirbt Prozess (aus irgendeinem Grund) der Elternprozess des Kindes wird Prozess 1. Wenn das Kind einfach Umfragen regelmäßig, kann es überprüfen, ob seine Eltern ist 1. Wenn ja, sollte das Kind verlassen.

Dies ist nicht groß, aber es funktioniert, und es ist einfacher, als die TCP-Socket / lockfile Polling Lösungen auf dieser Seite vorgeschlagen anderswo.

Ich habe dies in der Vergangenheit erreicht, indem den „Original“ Code in dem „Kind“ läuft und den „hervorgebracht“ Code in den „Eltern“ (das heißt: Sie können den üblichen Sinn des Tests nach fork() rückwärts). Dann Fall SIGCHLD im "hervorgebracht" Code ...

Auch in Ihrem Fall nicht möglich sein, aber nett, wenn es funktioniert.

Wenn Sie nicht auf das Kind Prozess ändern, können Sie so etwas wie die folgenden 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 */

Dies läuft das Kind aus einem Shell-Prozess mit Auftragssteuerung aktiviert. Das Kind Prozess wird im Hintergrund hervorgebracht. Die Shell wartet auf eine neue Zeile (oder ein EOF) dann tötet das Kind.

Wenn der Elternteil stirbt - egal, was der Grund - es sein Ende des Rohres wird geschlossen. Das Kind Shell wird einen EOF aus dem Lese bekommen und fahren Sie mit dem backgrounded Kind-Prozess zu töten.

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

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);
}

Hat das Kind Prozess ein Rohr zum / vom übergeordneten Prozess? Wenn ja, werden Sie einen SIGPIPE wenn das Schreiben erhalten, oder EOF erhalten beim Lesen -. Könnten diese Bedingungen nachgewiesen werden,

Unter Linux können Sie ein Elternteil Todessignal in dem 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 der übergeordnete Prozess-ID vor der Gabel zu speichern und in dem Kind nach dem prctl() eliminiert eine Racebedingung zwischen prctl() und der Ausgang des Prozesses, der das Kind genannt.

Beachten Sie auch, dass das Muttertodessignal des Kindes in neu geschaffenen Kindern ihrer eigenen gelöscht wird. Es ist nicht von einer execve() betroffen.

können Dieser Test vereinfacht werden, wenn wir sicher sind, dass das System Prozess, der von der Annahme aller Waisen hat PID 1:

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 ...

Unter Berufung auf dieses System Prozess ist init und mit PID 1 ist nicht tragbar, though. POSIX.1-2008 gibt :

  

Die übergeordnete Prozess-ID all vorhandenen Kindprozesse und Zombie-Prozesse des anrufenden Prozesses wird die Prozess-ID eines Implementierung definierten System-Prozesses eingestellt werden. Das heißt, werden diese Prozesse durch einen speziellen System-Prozess übernommen werden.

Traditionell ist der Systemprozess alle Waisen Annahme ist PID 1, das heißt init -., Die der Vorfahr aller Prozesse ist

Auf modernen Systemen wie Linux oder FreeBSD ein anderer Prozess diese Rolle haben könnte. Zum Beispiel auf Linux, kann ein Prozess nennen prctl(PR_SET_CHILD_SUBREAPER, 1) etablieren sich als Systemprozess, der alle Waisen jedes seiner Nachkommen vererbt (vgl ein Beispiel auf Fedora 25).

Inspiriert von einer anderen Antwort hier, kam ich auf der folgenden all-POSIX-Lösung auf. Die allgemeine Idee ist es, einen Zwischenprozess zwischen dem Elternteil und dem Kind zu schaffen, das einen Zweck hat:. Beachten Sie, wenn der Elternteil stirbt, und explizit das Kind töten

Diese Art der Lösung ist nützlich, wenn der Code in dem Kind 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);
}

Es gibt zwei kleine Einschränkungen mit dieser Methode:

  • Wenn Sie absichtlich den Zwischen Prozess töten, dann wird das Kind nicht getötet werden, wenn das Elternteil stirbt.
  • Wenn das Kind vor dem Elternteil verläßt, dann wird der Zwischen Prozess wird versuchen, das ursprüngliche Kind pid zu töten, die jetzt zu einem anderen Prozess beziehen könnten. (Dies könnte mit mehr Code in dem Zwischenprozess festgelegt werden.)

Als Nebenwirkung der eigentliche Code ich benutze in Python ist. 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)

Ich glaube nicht, es ist möglich, zu garantieren, dass nur Standard-POSIX-Aufrufe. Wie im richtigen Leben, sobald ein Kind hervorgebracht wird, hat es ein Eigenleben.

Es ist möglich, dass der übergeordnete Prozess möglichst Beendigung Ereignisse zu fangen, und versucht, das Kind Prozess an diesem Punkt zu töten, aber es gibt immer einige, die nicht gefangen werden können.

Zum Beispiel kann kein Verfahren zur Herstellung eines SIGKILL fängt. Wenn der Kernel dieses Signal behandelt wird es den angegebenen Prozess ohne Benachrichtigung zu diesem Prozess töten zu löschen.

die Analogie zu verlängern -. Die einzigen anderen Standardverfahren zu tun, es ist für das Kind, Selbstmord zu begehen, wenn sie feststellt, dass es nicht mehr einen Elternteil hat

Es ist ein Linux-einziger Weg, es mit prctl(2) zu tun -. Andere Antworten

Wie andere Leute haben darauf hingewiesen, auf der übergeordneten pid unter Berufung 1 zu werden, wenn die Eltern Ausfahrten nicht tragbar ist. Statt für eine bestimmte übergeordneten Prozess-ID des Wartens, nur warten, bis die ID zu ändern:

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 einen Mikro-Schlaf, wie gewünscht, wenn Sie nicht mit voller Geschwindigkeit abfragen wollen.

Diese Option erscheint mir einfacher als ein Rohr mit oder auf diese angewiesen Signale.

Installieren Sie einen Trap-Handler SIGINT zu fangen, die Ihr Kind Prozess tötet, wenn er noch am Leben ist, obwohl andere Plakate korrekt sind, dass es SIGKILL nicht fangen.

Öffnen Sie ein .lockfile mit exklusivem Zugang und haben das Kind Umfrage auf sie versuchen, es zu öffnen - wenn die offene erfolgreich ist, sollte das Kind Prozess beenden

Diese Lösung funktioniert für mich:

  • Pass stdin Rohr Kind -. Sie müssen keine Daten in den Stream schreiben
  • Kind liest auf unbestimmte Zeit von stdin bis EOF. Ein EOF signalisiert, dass die Eltern gegangen ist.
  • Dies ist narrensicher und tragbare Art und Weise zu erfassen, wenn die Eltern gegangen sind. Selbst wenn Eltern abstürzt, O wird das Rohr schließen.

Dies war für einen Arbeiter-Typen Prozess, deren Existenz nur Sinn, wenn die Eltern noch am Leben waren.

Ich denke, eine schnelle und schmutzige Art und Weise ist es, ein Rohr zwischen Kind und Eltern zu schaffen. Wenn Eltern verlässt, werden die Kinder eine SIGPIPE erhalten.

haben einige Plakate bereits Rohre und kqueue erwähnt. In der Tat können Sie auch ein Paar verbunden Unix-Domain-Sockets durch den socketpair() Anruf erstellen. Der Socket-Typ sollte SOCK_STREAM werden.

Nehmen wir an, Sie die zwei Socket-Datei-Deskriptoren FD1 haben, FD2. Jetzt fork() das Kind Prozess zu schaffen, die die fds erben. In der Mutter schließen Sie FD2 und in dem Kind, das Sie schließen FD1. Nun kann jeder Prozess die verbleibenden offenen fd auf seinem eigenen Ende für das poll() Ereignis POLLIN. Solange jede Seite nicht explizit seine fd während der normalen Lebensdauer nicht close(), können Sie ziemlich sicher sein, dass eine POLLHUP Flagge des Terminierung des anderen zeigen soll (egal sauber oder nicht). Bei dieser Veranstaltung gemeldet, kann das Kind entscheiden, was zu tun ist (zum Beispiel, um 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 oben Proof-of-Concept-Code kompiliert, und es in einem Terminal wie ./a.out & läuft. Sie haben rund 100 Sekunden mit Tötung der Eltern PID durch verschiedene Signale zu experimentieren, oder es wird einfach verlassen. In jedem Fall sollten Sie die Meldung „Kind: Eltern aufgehängt“ sehen

.

mit der Methode im Vergleich SIGPIPE Handler, dieses Verfahren erfordert nicht den write() Anruf versucht.

Diese Methode ist auch symmetrisch , das heißt, die Prozesse, die denselben Kanal verwenden können, sich gegenseitig die Existenz zu überwachen.

Diese Lösung erfordert nur die POSIX-Funktionen. Ich habe versucht, dies in Linux und FreeBSD. Ich denke, es sollte auf anderen Unix-Varianten arbeiten, aber ich habe nicht wirklich getestet.

Siehe auch:

  • unix(7) von Linux Manpages, unix(4) für FreeBSD, poll(2), socketpair(2), socket(7) auf Linux.

Unter POSIX, Die exit(), _exit() Und _Exit() Funktionen sind definiert als:

  • Wenn es sich bei dem Prozess um einen steuernden Prozess handelt, muss das SIGHUP-Signal an jeden Prozess in der Vordergrundprozessgruppe des steuernden Terminals gesendet werden, das zum aufrufenden Prozess gehört.

Wenn Sie also festlegen, dass der übergeordnete Prozess ein steuernder Prozess für seine Prozessgruppe ist, sollte der untergeordnete Prozess ein SIGHUP-Signal erhalten, wenn der übergeordnete Prozess beendet wird.Ich bin mir nicht ganz sicher, ob das passiert, wenn das übergeordnete Element abstürzt, aber ich denke, dass es so ist.Sicherlich sollte es in den Fällen, in denen es nicht zu einem Absturz kommt, gut funktionieren.

Beachten Sie, dass Sie möglicherweise ziemlich viel Kleingedrucktes lesen müssen – einschließlich des Abschnitts „Basisdefinitionen“ (Definitionen) sowie der Informationen zu Systemdiensten exit() Und setsid() Und setpgrp() - um ein vollständiges Bild zu erhalten.(Das würde ich auch tun!)

Wenn Sie senden ein Signal an die pid 0, zum Beispiel mit

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

das Signal an die gesamte Prozessgruppe gesendet wird, so dass das Kind wirksam zu töten.

Sie können es testen leicht mit so etwas wie:

(cat && kill 0) | python

Wenn Sie dann ^ D drücken, werden Sie den Text "Terminated" als Hinweis sehen, dass der Python-Interpreter tatsächlich getötet wurden, anstatt nur verlassen, weil von stdin geschlossen wird.

Falls es jemand anderem relevant, wenn ich JVM-Instanzen in gegabelten Kindprozesse von C ++ laichen, die einzige Art, wie ich die JVM-Instanzen ordnungsgemäß zu beenden, bekommen konnte, nachdem der übergeordnete Prozess abgeschlossen war folgendes zu tun. Hoffentlich kann jemand Feedback in den Kommentaren zur Verfügung stellen, wenn dies nicht der beste Weg, dies zu tun.

1) Ruf prctl(PR_SET_PDEATHSIG, SIGHUP) auf dem Kind Prozess gegabelt wie vorgeschlagen, bevor Sie die Java-Anwendung über execv starten und

2) In der Java-Anwendung einen Shutdown-Haken, die Urnen, bis seine Eltern PID gleich 1, dann eine harte Runtime.getRuntime().halt(0) tun. Die Abfrage erfolgt durch eine separate Shell gestartet, die den ps Befehl ausführt (siehe: Wie finde ich meine PID in Java oder JRuby auf Linux? ).

EDIT 130118:

Es scheint, dass nicht eine robuste Lösung. Ich kämpfe immer noch ein wenig die Nuancen zu verstehen, was los ist, aber ich war immer noch manchmal Waise JVM Prozesse bekommen, wenn diese Anwendungen in Bildschirm / SSH-Sitzungen ausgeführt werden.

Statt Polling für die PPID in der Java-Anwendung, ich hatte einfach die Shutdown-Hook Bereinigung von einer harten halt wie oben gefolgt auszuführen. Dann machte ich sicher waitpid in der C ++ Eltern-App auf dem gelaicht Kind Prozess aufzurufen, wenn es Zeit, um alles zu beenden war. Dies scheint eine robustere Lösung zu sein, da das Kind Prozess stellt sicher, dass es endet, während die Eltern bestehende Referenzen verwendet, um sicherzustellen, dass seine Kinder beenden. Vergleichen Sie dies mit der bisherigen Lösung, die der übergeordnete Prozess hatte zu beenden, wann immer ihm gefiel, und hatte die Kinder versuchen, herauszufinden, ob sie vor dem Beenden verwaist worden war.

Wenn Elternteil stirbt, ändern PPID von Waisen zu 1 - Sie müssen nur Ihre eigene PPID überprüfen. In gewisser Weise ist diese Abfrage, wie oben erwähnt. hier ist Schalenstü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

Ich fand zwei Lösungen, die beide nicht perfekt.

1.Kill alle Kinder durch kill (-pid), wenn SIGTERM-Signal empfangen.
Offensichtlich kann diese Lösung nicht umgehen „kill -9“, aber es tut Arbeit für die meisten Fälle und sehr einfach, weil sie nicht brauchen, um alle untergeordneten Prozesse zu erinnern.


    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();
      });
    }

Mit dem gleichen Weg können Sie ‚exit‘ Handler wie oben Art und Weise installieren, wenn Sie process.exit irgendwo anrufen. . Hinweis: Strg + C und plötzlicher Absturz werden automatisch von O verarbeitet Prozessgruppe zu töten, so dass nicht mehr hier

chjj / pty.js Ihren Prozess zum Laichen mit Steuerung Klemme befestigt.
Wenn Sie mit dem aktuellen Prozess töten sowieso noch -9 töten, werden alle untergeordneten Prozesse automatisch auch getötet werden (von OS?). Ich denke, dass da aktuelle Prozess eine andere Seite des Terminals halten, so dass, wenn aktuelle Prozess stirbt, wird das Kind Prozess bekommen SIGPIPE so 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(.....);
    */

konnte ich mit drei Prozessen eine tragbare, nicht-Polling-Lösung tun, indem sie zu missbrauchen Terminal-Steuerung und Sitzungen. Dies ist mentale Masturbation, aber funktioniert.

Der Trick ist:

  • Prozess A gestartet
  • Prozess A erzeugt ein Rohr P (und nie liest von ihm)
  • Prozess A Gabeln in Verfahren B
  • Prozess B erstellt eine neue Sitzung
  • Prozess B weist ein virtuelles Terminal für die neue Sitzung
  • Prozess B installiert SIGCHLD Handler zu sterben, wenn das Kind verlässt
  • Prozess B setzt ein SIGPIPE Handler
  • Prozess B Gabeln in Verfahren C
  • Prozess C tut, was es braucht (zum Beispiel exec () s das nicht modifizierte binäre oder läuft unabhängig von Logik)
  • Prozess B schreibt Rohr P (und blockiert auf diese Weise)
  • Prozess A wait () s auf Prozess B und tritt, wenn er stirbt

Auf diese Weise:

  • , wenn Prozess A stirbt: Verfahren B eine SIGPIPE bekommt und stirbt
  • , wenn das Verfahren B stirbt: Prozess A Warten () zurückkehrt und stirbt, Verfahren C wird ein SIGHUP (weil, wenn die Sitzung Führer einer Sitzung mit einem befestigten Gesenke, alle Prozesse im Vordergrund Prozessgruppe ein SIGHUP erhalten)
  • , wenn Prozess C stirbt: Verfahren B eine SIGCHLD bekommt und stirbt, so stirbt Prozess A

Mangel:

  • Prozess C nicht verarbeiten kann SIGHUP
  • Prozess C in einer anderen Sitzung ausgeführt wird, wird
  • Prozess C kann nicht Sitzungs- / Prozessgruppe API verwenden, da sie die spröde Setup brechen werden
  • ein Terminal für jeden einen solchen Betrieb zu schaffen ist nicht die beste Idee überhaupt

Obwohl 7 Jahre vergangen Ich habe gerade in diese Frage führen, wie ich SpringBoot Anwendung renne, die webpack-dev-Server während der Entwicklung beginnen muss und es muss töten, wenn der Back-End-Prozess beendet.

Ich versuche Runtime.getRuntime().addShutdownHook zu verwenden, aber es hat funktioniert unter Windows 10, aber nicht auf Windows 7.

Ich habe es ändern, um einen speziellen Thread zu verwenden, die für den Prozess zu beenden oder für InterruptedException wartet, die korrekt auf beiden Windows-Versionen zu funktionieren scheinen.

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();
}

Historisch gesehen, von UNIX v7, das Prozesssystem wurde durch Prüfen einer Prozess Parent ID orphanity von Prozessen erkannt wird. Wie gesagt, historisch gesehen, ist der init(8) Systemprozess ein spezielles Verfahren durch nur einen Grund: Es kann nicht sterben. Es kann nicht sterben, weil der Kernel-Algorithmus mit Zuweisung eines neuen übergeordneten Prozess-ID zu behandeln, hängt von dieser Tatsache. wenn ein Prozess seine exit(2) Aufruf ausführt (mittels eines Prozesssystemaufruf oder durch externe Aufgabe, wie es ein Signal oder dergleichen zu senden) neu zuweist der Kernel alle Kinder dieses Verfahren die ID des Init-Prozeß als ihren Eltern-Prozess-ID. Dies führt zu dem einfachste Test, und die meisten tragbare Möglichkeit zu wissen, ob ein Prozess Waise erhalten hat. Stellt sicher, das Ergebnis der getppid(2) Systemaufruf und wenn es die Prozess-ID des init(2) Prozess ist der Prozess dann bekam Waise vor dem Systemaufruf.

Zwei Fragen aus diesem Ansatz entstehen, die zu Problemen führen können:

  • Erstens haben wir die Möglichkeit, den Prozess init jedem Benutzer Prozess der Veränderung, so Wie können wir versichern, dass der init-Prozess aller verwaisten Prozesse immer Elternteil sein? Nun, in der exit Systemaufruf Code gibt es eine explizite Überprüfung, ob der Prozess den Aufruf der Ausführung der init-Prozess (der Prozess mit pid gleich 1), und wenn das der Fall ist, die Kernel-Fehler (Es sollte nicht mehr in der Lage zu hält die Prozesshierarchie), so ist es nicht für den init-Prozess erlaubt einen exit(2) Aufruf zu tun.
  • zweitens gibt es eine Race-Bedingung in dem Basistest oben ausgesetzt. Init-Prozess id wird angenommen, historisch zu 1, aber das ist nicht von dem POSIX-Ansatz garantiert, dass die Staaten (wie in anderer Reaktion ausgesetzt), die nur ein Systems Prozess-ID für diesen Zweck reserviert ist. Fast keine Posix-Implementierung tut dies, und Sie können in ursprünglichen Unix-basierten Systemen übernehmen, die 1 als Antwort von getppid(2) Systemaufruf mit genug ist, um das Verfahren zu übernehmen Waise ist. Ein anderer Weg, um zu überprüfen ist, um ein getppid(2) zu machen kurz nach der Gabel und vergleicht diesen Wert mit dem Ergebnis eines neuen Anruf. Das funktioniert einfach nicht in allen Fällen funktionieren, da beide Aufruf zusammen nicht atomar ist, und der übergeordnete Prozess nach dem fork(2) und vor dem ersten getppid(2) Systemaufruf sterben kann. Die processparent id only changes once, when its parent does anexit (2) call, so this should be enough to check if thegetppid (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 ofinit (8) `, aber Sie können als nicht Elternteil entweder (außer, wenn Sie in einem System, den Init-Prozess ersetzen) sicher, diese Prozesse übernehmen

Ein andere Möglichkeit, dies zu tun, die Linux-spezifisch ist, ist die Mutter hat in einem neuen PID-Namespace erstellt werden. Es wird dann PID sein 1 in diesem Namensraum, und wenn es sie alle seine Kinder verlässt sofort mit SIGKILL getötet werden.

Leider, um einen neuen PID-Namespace Sie haben CAP_SYS_ADMIN haben zu erstellen. Aber ist diese Methode sehr effektiv und erfordert keine wirkliche Änderung der Eltern oder die Kinder über die ursprüngliche Einführung des Elternteils.

Siehe Klon (2) pid_namespaces (7) und unshare (2) .

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top