Jak sprawić, by proces potomny umarł po wyjściu rodzica?

Przypuśćmy, że mam proces, który rodzi dokładnie jeden proces potomny. Teraz, gdy proces macierzysty zakończy się z jakiegokolwiek powodu (normalnie lub nienormalnie, przez kill, ^c, assert failure lub cokolwiek innego) chcę, aby proces potomny umarł. Jak to zrobić poprawnie?


Podobne pytanie na stackoverflow:


Podobne pytanie o stackoverflow dla Windows :

Author: Community, 2008-11-12

23 answers

Dziecko może poprosić jądro o dostarczenie SIGHUP (lub innego sygnału), gdy rodzic umiera, podając opcję PR_SET_PDEATHSIG w prctl() syscall w następujący sposób:

prctl(PR_SET_PDEATHSIG, SIGHUP);

Zobacz man 2 prctl Po szczegóły.

Edit: This is Linux-only

 164
Author: qrdl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-13 18:25:04

Próbuję rozwiązać ten sam problem, a ponieważ mój program musi działać na OS X, rozwiązanie tylko dla Linuksa nie działało dla mnie.

Doszedłem do tego samego wniosku co inni ludzie na tej stronie -- nie ma zgodnego z POSIX sposobu powiadamiania dziecka o śmierci rodzica. Więc wymyśliłem następną, najlepszą rzecz-przeprowadzenie ankiety dla dzieci.

Gdy proces rodzic umiera (z jakiegokolwiek powodu) proces rodzic dziecka staje się procesem 1. Jeśli dziecko po prostu okresowo sonduje, może sprawdzić jeśli jego rodzicem jest 1. Jeśli tak, dziecko powinno wyjść.

To nie jest świetne, ale działa i jest łatwiejsze niż rozwiązania TCP socket / lockfile sugerowane gdzie indziej na tej stronie.

 59
Author: Schof,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-10 01:18:08

Osiągnąłem to w przeszłości, uruchamiając "oryginalny" kod w "dziecku " i" Zrodzony "kod w "rodzicu"(to znaczy: odwracasz zwykły sens testu po fork()). Następnie trap SIGCHLD w kodzie "spawned"...

Może nie jest to możliwe w Twoim przypadku, ale słodkie, gdy to działa.

 33
Author: dmckee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-12 19:58:48

Jeśli nie możesz zmodyfikować procesu potomnego, możesz spróbować czegoś takiego:

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 */

Uruchamia proces potomny z poziomu powłoki z włączoną kontrolą zadań. Proces potomny jest wywoływany w tle. Powłoka czeka na znak nowej linii (lub EOF), a następnie zabija dziecko.

Kiedy rodzic umrze, bez względu na powód, zamknie swój koniec rury. Powłoka potomna otrzyma EOF z procesu read i przystąpi do zabicia ukrytego procesu potomnego.
 28
Author: Phil Rutschman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-29 17:38:01

Ze względu na kompletność. Na macOS możesz użyć 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);
}
 15
Author: neoneye,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-07 15:12:11

Czy proces potomny ma łącze do / z procesu rodzica? Jeśli tak, otrzymasz SIGPIPE podczas pisania lub get EOF podczas czytania - te warunki mogą zostać wykryte.

 10
Author: MarkR,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-12 16:15:49

Pod Linuksem można zainstalować sygnał śmierci rodzica w dziecku, np.:

#include <sys/prctl.h>
#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 ...

Zauważ, że przechowywanie ID procesu nadrzędnego przed forkiem i testowanie go w potomku po prctl() eliminuje stan wyścigu pomiędzy prctl() i wyjście z procesu, który wezwał dziecko.

Zauważ również, że sygnał śmierci rodzica dziecka jest usuwany w nowo utworzonych dzieciach. Nie ma na nią wpływu execve().

Ten test można uprościć, jeśli są pewne, że proces systemowy, który jest odpowiedzialny za adopcję wszystkichsierot ma 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 ...

Poleganie na tym, że proces systemowy jest init i posiadanie PID 1 nie jest jednak przenośne. POSIX.1-2008:

Identyfikator procesu nadrzędnego wszystkich istniejących procesów potomnych i procesów zombie procesu wywołującego zostanie ustawiony na identyfikator procesu systemowego zdefiniowanego w implementacji. Oznacza to, że procesy te są dziedziczone przez specjalny proces systemowy.

Tradycyjnie proces systemowy przyjmujący wszystkie sieroty to PID 1, czyli INIT-który jest przodkiem wszystkich procesów.

Na nowoczesnych systemach, takich jak Linux lub FreeBSD inny proces może pełnić tę rolę. Na przykład w Linuksie proces może wywołać prctl(PR_SET_CHILD_SUBREAPER, 1) ustanowienie się jako proces systemowy, który dziedziczy wszystkie sieroty po którymkolwiek z jego potomków (por. przykład na Fedorze 25).

 10
Author: maxschlepzig,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-28 18:46:15

Zainspirowany inną odpowiedzią tutaj, wymyśliłem następujące rozwiązanie all-POSIX. Ogólną ideą jest stworzenie procesu pośredniego między rodzicem a dzieckiem, który ma jeden cel: zauważyć, kiedy rodzic umiera, i wyraźnie zabić dziecko.

Tego typu rozwiązanie jest przydatne, gdy kod w potomku nie może być modyfikowany.

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

Istnieją dwa małe zastrzeżenia dotyczące tej metody:

  • jeśli celowo zabijasz proces pośredni, wtedy dziecko nie zginie, gdy rodzic umrze.
  • jeśli potomek wyjdzie przed rodzicem, to proces pośredni spróbuje zabić oryginalny PID potomka, który może teraz odnosić się do innego procesu. (Można to naprawić za pomocą większej ilości kodu w procesie pośrednim.)

Na marginesie, rzeczywisty kod, którego używam, jest w Pythonie. Tutaj jest dla kompletności:

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)
 9
Author: Greg Hewgill,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-01 02:25:57

Nie wydaje mi się, aby można było to zagwarantować używając tylko standardowych wywołań POSIX. Jak w prawdziwym życiu, kiedy dziecko rodzi się, ma własne życie.

To jest możliwe, aby proces macierzysty wychwycił większość możliwych zdarzeń zakończenia i próbował zabić proces potomny w tym momencie, ale zawsze są takie, których nie można złapać.

Na przykład żaden proces nie może złapać SIGKILL. Gdy jądro obsłuży ten sygnał, zabije określony proces bez powiadomienie o tym procesie.

Aby rozszerzyć analogię - jedynym standardowym sposobem jest popełnienie samobójstwa przez dziecko, gdy stwierdzi, że nie ma już rodzica.

Jest tylko Linuksowy sposób na zrobienie tego z prctl(2) - Zobacz inne odpowiedzi.

 7
Author: Alnitak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-13 08:37:26

Zainstaluj trap handler , aby złapać SIGINT, który zabija proces potomny, jeśli nadal żyje, chociaż inne plakaty są poprawne, że nie złapie SIGKILL.

Otwórz a .lockfile z wyłącznym dostępem i niech ankieta potomna próbuje go otworzyć - jeśli otwarcie się powiedzie, proces potomny powinien zakończyć

 4
Author: Paul Betts,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-12 16:42:06

Jak zauważyli inni ludzie, poleganie na tym, że PID rodzica stanie się 1, gdy wyjście rodzica nie jest przenośne. Zamiast czekać na konkretny identyfikator procesu nadrzędnego, po prostu poczekaj na zmianę identyfikatora:

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

Dodaj mikro-sen zgodnie z życzeniem, jeśli nie chcesz sondować na pełnej prędkości.

Ta opcja wydaje mi się prostsza niż używanie rury lub poleganie na sygnałach.

 4
Author: user2168915,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-21 00:49:22

Myślę, że szybkim i brudnym sposobem jest stworzenie rury między dzieckiem a rodzicem. Po wyjściu rodzica dzieci otrzymają SIGPIPE.

 3
Author: Yefei,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-09 08:25:14

To rozwiązanie zadziałało dla mnie:

  • przeprowadź stdin pipe do potomka - nie musisz zapisywać żadnych danych do strumienia.
  • dziecko czyta w nieskończoność od stdin do EOF. EOF sygnalizuje, że rodzic odszedł.
  • To niezawodny i przenośny sposób na wykrycie, kiedy rodzic odszedł. Nawet jeśli rodzic ulegnie awarii, system operacyjny zamknie rurę.

Był to proces typu robotniczego, którego istnienie miało sens tylko wtedy, gdy rodzic żył.

 3
Author: joonas.fi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-03 06:57:22

Niektóre plakaty wspominały już o rurach i kqueue. W rzeczywistości można również utworzyć parę połączonych gniazd domeny Unix przez wywołanie socketpair(). Typ gniazda powinien być SOCK_STREAM.

Załóżmy, że masz dwa deskryptory plików gniazd fd1, fd2. Teraz fork() Aby utworzyć proces potomny, który odziedziczy fds. W rodzicu zamykasz fd2, a w dziecku zamykasz fd1. Teraz kaĹźdy proces moĹźe poll() pozostaĹ 'y otwarty fd na swoim koĹ" cu dla zdarzenia POLLIN. As long ponieważ każda strona nie ma jawnie close() swojego fd podczas normalnego życia, możesz być całkiem pewien, że flaga POLLHUP powinna wskazywać zakończenie drugiej strony (bez względu na to, czy jest czysta, czy nie). Po powiadomieniu o tym zdarzeniu dziecko może zdecydować, co ma zrobić (np. umrzeć).

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

Możesz spróbować skompilować powyższy kod proof-of-concept i uruchomić go w terminalu takim jak ./a.out &. Masz około 100 sekund na eksperymentowanie z uśmiercaniem PID rodzicielskiego przez różne sygnały, lub po prostu zakończy się. W obu przypadkach, powinieneś zobaczyć komunikat "dziecko: rodzic rozłączył się".

W porównaniu z metodą używającą obsługi SIGPIPE, ta metoda nie wymaga próbowania wywołania write().

Metoda ta jest również symetryczna, tzn. procesy mogą używać tego samego kanału do monitorowania swojego istnienia.

To rozwiązanie wywołuje tylko funkcje POSIX. Próbowałem tego w Linuksie i FreeBSD. Myślę, że powinno działać na innych Unixach, ale tak naprawdę nie testowałem.

Zobacz również:

  • unix(7) Linuksowych stron man, unix(4) Dla FreeBSD, poll(2), socketpair(2), socket(7) na Linuksie.
 2
Author: Cong Ma,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-13 05:12:35

Under POSIX , the exit(), _exit() and _Exit() funkcje są zdefiniowane jako:

  • jeśli Proces jest procesem sterującym, sygnał SIGHUP jest wysyłany do każdego procesu w grupie procesów pierwszoplanowych terminala sterującego należącego do procesu wywołującego.

Tak więc, jeśli ustawisz proces macierzysty jako proces kontrolujący jego grupę procesów, dziecko powinno otrzymać sygnał SIGHUP, gdy rodzic zakończy działanie. Nie jestem do końca pewien, czy to się dzieje, gdy rodzic się wywala, ale myślę, że tak. Oczywiście, w przypadku spraw bezwypadkowych, powinno działać dobrze.

Zauważ, że być może będziesz musiał przeczytać sporo drobnym drukiem-w tym sekcję definicje podstawowe (definicje), a także informacje o usługach systemowych dla exit() i setsid() i setpgrp() - aby uzyskać pełny obraz. (Ja też!)

 1
Author: Jonathan Leffler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-07 15:35:11

Jeśli wyślesz sygnał do pid 0, używając na przykład

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

Ten sygnał jest wysyłany do całej grupy procesów, tym samym skutecznie zabijając dziecko.

Możesz go łatwo przetestować za pomocą czegoś takiego:

(cat && kill 0) | python

Jeśli naciśniesz ^d, zobaczysz tekst "Terminated" jako wskazówkę, że interpreter Pythona rzeczywiście został zabity, zamiast po prostu wyjść z powodu zamknięcia standardowego wejścia.

 1
Author: Thorbiörn Fritzon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-05 21:31:47

W przypadku, gdy jest to istotne dla kogokolwiek innego, kiedy odradzam instancje JVM w rozwidlonych procesach potomnych z C++, jedynym sposobem na poprawne zakończenie instancji JVM po zakończeniu procesu macierzystego było wykonanie następujących czynności. Mam nadzieję, że ktoś może przekazać opinię w komentarzach, jeśli nie był to najlepszy sposób na to.

1) wywołanie prctl(PR_SET_PDEATHSIG, SIGHUP) Na rozwidlonym procesie potomnym, zgodnie z sugestią przed uruchomieniem aplikacji Java przez execv i

2) Dodaj hak zamykania aplikacji Java jeśli PID rodzica jest równy 1, Wykonaj twardą Runtime.getRuntime().halt(0). Ankieta odbywa się poprzez uruchomienie oddzielnej powłoki, która uruchamia polecenie ps (Zobacz: Jak znaleźć mój PID w Javie lub JRuby na Linuksie?).

Edytuj 130118:

Wydaje się, że nie było to solidne rozwiązanie. Wciąż trochę staram się zrozumieć niuanse tego, co się dzieje, ale nadal czasami otrzymywałem osierocone procesy JVM podczas uruchamiania tych aplikacji w sesjach screen / SSH.

Zamiast sondowania dla PPID w aplikacji Java, po prostu miałem hak zamykania wykonać czyszczenie, a następnie twarde zatrzymanie jak powyżej. Następnie upewniłem się, że wywołałem waitpid w aplikacji macierzystej C++ w procesie potomnym, gdy nadszedł czas, aby zakończyć wszystko. Wydaje się to być bardziej solidnym rozwiązaniem, ponieważ proces potomny zapewnia jego zakończenie, podczas gdy rodzic używa istniejących odniesień, aby upewnić się, że jego dzieci zakończą. Porównaj to z poprzednim rozwiązaniem, które miało proces rodzica kończy się, gdy tylko się spodoba, i gdyby dzieci próbowały dowiedzieć się, czy zostały osierocone przed zakończeniem.

 1
Author: jasterm007,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:32

Mimo, że minęło 7 lat, właśnie natknąłem się na ten problem, ponieważ uruchamiam aplikację SpringBoot, która musi uruchomić webpack-dev-server podczas rozwoju i musi ją zabić, gdy proces backend zatrzyma się.

Staram się używać Runtime.getRuntime().addShutdownHook ale to działało na Windows 10, ale nie na Windows 7.

Zmieniłem go, aby użyć dedykowanego wątku, który czeka na zakończenie procesu lub na InterruptedException, który wydaje się działać poprawnie w obu wersjach systemu Windows.

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();
}
 1
Author: Ido Ran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-25 11:24:00

Jeśli rodzic umrze, PPID sierot zmieni się na 1 - wystarczy tylko sprawdzić swój własny PPID. W pewnym sensie jest to sondaż, o którym mowa powyżej. oto kawałek powłoki do tego:

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
Author: alex K,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-06 23:17:22

Znalazłem 2 rozwiązania, oba nie idealne.

1.Zabij wszystkie dzieci przez kill (- pid) po odebraniu sygnału SIGTERM.
Oczywiście, to rozwiązanie nie może obsłużyć "kill -9", ale działa w większości przypadków i bardzo proste, ponieważ nie trzeba pamiętać wszystkich procesów potomnych.


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

W ten sam sposób, możesz zainstalować obsługę 'exit' jak wyżej, jeśli wywołasz proces.wyjdź gdzieś. Uwaga: Ctrl + C i nagła awaria zostały automatycznie przetworzone przez system operacyjny w celu zabicia grupy procesów, więc nie więcej tutaj.

2.Użyj chjj / pty.js to spawn your process with controlling terminal attached.
Po zabiciu bieżącego procesu nawet przez kill -9, wszystkie procesy potomne również zostaną automatycznie zabite (przez OS?). Domyślam się, że ponieważ bieżący proces trzyma drugą stronę terminala, więc jeśli bieżący proces umrze, proces potomny otrzyma SIGPIPE, więc umrze.


    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
Author: osexp2003,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-24 04:12:17

Udało mi się zrobić przenośne, niepotwierdzone rozwiązanie z 3 procesami, nadużywając kontroli terminali i sesji. To jest mentalna Masturbacja, ale działa.

Sztuczka jest:

  • rozpoczyna się proces A
  • proces a tworzy rurę P (i nigdy z niej nie czyta)
  • proces a rozwidla się w proces B
  • proces B tworzy nową sesję
  • proces B przydziela wirtualny terminal dla nowej sesji
  • proces b instaluje funkcję obsługi SIGCHLD, aby umarła, gdy wyjście dziecka
  • proces B ustawia obsługę SIGPIPE
  • proces B rozwidla się w proces C
  • proces C robi wszystko, czego potrzebuje (np. exec () s niezmodyfikowany binarny lub uruchamia dowolną logikę)
  • proces B zapisuje do rury P (i blokuje w ten sposób)
  • proces a wait () s na procesie b i kończy proces gdy umiera

W ten sposób:

  • jeśli proces A umiera: proces B otrzymuje SIGPIPE i umiera
  • jeśli proces B umiera: proces a zwraca wait () I umiera, proces C SIGHUP (ponieważ gdy lider sesji z dołączonym terminalem umiera, wszystkie procesy w grupie procesów pierwszoplanowych otrzymują SIGHUP)
  • jeśli proces C umiera: proces B otrzymuje SIGCHLD i umiera, więc proces A umiera

Braki:

  • proces C nie radzi sobie z SIGHUPEM
  • proces C zostanie uruchomiony w innej sesji
  • proces C nie może używać API grupy sesji / procesów, ponieważ spowoduje to złamanie konfiguracji kruchej
  • Tworzenie terminala dla każda taka operacja nie jest najlepszym pomysłem]}
 0
Author: Marek Dopiera,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-21 16:22:06

Historycznie, od wersji UNIX v7, system procesowy wykrywał orphansity procesów poprzez sprawdzanie identyfikatora nadrzędnego procesu. Jak mówię, historycznie, init(8) proces systemowy jest specjalnym procesem tylko z jednego powodu: nie może umrzeć. Nie może umrzeć, ponieważ od tego zależy algorytm jądra, który ma do czynienia z przypisaniem nowego ID procesu macierzystego. gdy proces wykonuje swoje wywołanie exit(2) (za pomocą wywołania systemowego procesu lub zewnętrznego zadania jako wysłanie mu sygnału lub tym podobne), jądro ponownie przydziela wszystkie dzieci tego procesu id procesu init jako ich id procesu macierzystego. Prowadzi to do najprostszego testu i najbardziej przenośnego sposobu dowiedzenia się, czy proces został osierocony. Po prostu sprawdź wynik wywołania systemowego getppid(2) i jeśli jest to identyfikator procesu init(2), to proces został osierocony przed wywołaniem systemowym.

Z tego podejścia wyłaniają się dwie kwestie, które mogą prowadzić do problemów:]}
  • Po pierwsze, mamy możliwość zmiany procesu init na dowolnego użytkownika proces, więc jak możemy zapewnić, że proces init będzie zawsze rodzicem wszystkich procesów osieroconych? Cóż, w kodzie wywołania systemowego exit jest wyraźne sprawdzenie, czy proces wykonujący to wywołanie jest procesem init (proces z pid równym 1) i jeśli tak jest, jądro panikuje (nie powinno być już w stanie utrzymać hierarchii procesów), więc nie jest dozwolone, aby proces init wykonał wywołanie exit(2).
  • Po drugie, jest stan rasy w podstawowym teście wystawione powyżej. Historycznie przyjmuje się, że id procesu Init to 1, ale nie jest to uzasadnione przez podejście POSIX, które stwierdza (jako ujawnione w innej odpowiedzi), że tylko identyfikator procesu systemowego jest zarezerwowany do tego celu. Prawie żadna implementacja posix tego nie robi i można założyć w oryginalnych systemach pochodnych Uniksa, że posiadanie {[7] } jako odpowiedzi wywołania systemowego jest wystarczające, aby założyć, że proces jest osierocony. Innym sposobem sprawdzenia jest wykonanie getppid(2) tuż za widelcem i porównanie tej wartości z wynik nowego połączenia. To po prostu nie działa we wszystkich przypadkach, ponieważ oba wywołania nie są atomowe razem, a proces macierzysty może umrzeć po fork(2) i przed pierwszym wywołaniem systemowym getppid(2). Proces parent id only changes once, when its parent does an exit(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)`, ale możesz bezpiecznie założyć, że te procesy również nie mają rodzica (chyba że zastąpisz w systemie proces init)
 0
Author: Luis Colorado,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-08 08:59:54

Innym sposobem na to, który jest specyficzny dla Linuksa, jest utworzenie rodzica w nowej przestrzeni nazw PID. Następnie będzie to PID 1 w tej przestrzeni nazw, a kiedy wyjdzie z niej wszystkie jego dzieci zostaną natychmiast zabite za pomocą SIGKILL.

Niestety, aby utworzyć nową przestrzeń nazw PID musisz mieć CAP_SYS_ADMIN. Ale ta metoda jest bardzo skuteczna i nie wymaga prawdziwej zmiany rodzica lub dzieci poza początkowym uruchomieniem rodzica.

Zobacz klon (2) , pid_namespaces (7) i unshare(2).

 0
Author: Omnifarious,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-06 18:20:55