Comment faire mourir l'enfant après la sortie du parent?

Supposons que j'ai un processus qui engendre exactement un processus enfant. Maintenant, quand le processus parent sort pour quelque raison que ce soit (normalement ou anormalement, par tuer, ^C, affirmer l'échec ou autre chose) je veux que le processus enfant meurt. Comment faire ça correctement?


d'une même question sur stackoverflow:

  • (question posée plus tôt) Comment puis-je faire en sorte qu'un processus de l'enfant se termine lorsque le parent le fait?
  • (question plus loin) les processus enfants créés avec fork () sont-ils automatiquement tués lorsque le parent est tué?

une question similaire sur stackoverflow pour Windows :

  • comment détruire automatiquement les processus enfants sous Windows?
  • Tuer l'enfant processus lorsque le processus parent est tué
183
demandé sur Community 2008-11-12 18:37:51

23 réponses

L'enfant peut demander au noyau de délivrer SIGHUP (ou un autre signal) lorsque le parent meurt en spécifiant l'option PR_SET_PDEATHSIG dans prctl() syscall comme ceci:

prctl(PR_SET_PDEATHSIG, SIGHUP);

voir man 2 prctl pour plus de détails.

Edit: C'est Linux seulement

164
répondu qrdl 2008-11-13 18:25:04

j'essaie de résoudre le même problème, et puisque mon programme doit tourner sur OS X, la solution Linux-only n'a pas fonctionné pour moi.

j'en suis venu à la même conclusion que les autres personnes sur cette page -- il n'y a pas de façon compatible avec POSIX pour prévenir un enfant quand un parent meurt. J'ai donc cherché la meilleure solution: le sondage des enfants.

Lorsqu'un processus parental meurt (pour quelque raison que ce soit), le processus parental de l'enfant devient un processus 1. Si l'enfant simplement des sondages régulièrement, il peut vérifier si son parent est de 1. Si elle l'est, l'enfant doit quitter.

ce n'est pas génial, mais ça marche, et c'est plus facile que les solutions de sondage TCP socket/lockfile suggérées ailleurs sur cette page.

59
répondu Schof 2010-01-10 01:18:08

j'ai réalisé cela dans le passé en exécutant le code" original "dans le" child "et le code" spawned "dans le" parent "(c'est-à-dire: vous inversez le sens habituel du test Après fork() ). Puis piéger SIGCHLD dans le code" spawned"...

Peut ne pas être possible dans votre cas, mais mignon quand il fonctionne.

33
répondu dmckee 2008-11-12 19:58:48

si vous n'êtes pas en mesure de modifier le processus enfant, vous pouvez essayer quelque chose comme:

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

Cela fait fonctionner l'enfant à partir d'un processus shell avec contrôle de travail activé. Le processus de l'enfant est engendré à l'arrière-plan. La coquille attend une nouvelle ligne (ou un EOF) puis tue l'enfant.

quand le parent meurt,peu importe la raison, il ferme son extrémité du tuyau. L'enfant shell obtiendra un EOF de la lecture et procéder pour tuer le backgrounded processus enfant.

28
répondu Phil Rutschman 2010-10-29 17:38:01

Par souci d'exhaustivité. Sur macOS vous pouvez utiliser 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
répondu neoneye 2018-07-07 15:12:11

le processus enfant ont un tuyau vers/à partir du processus parent? Si c'est le cas, vous recevrez un SIGPIPE si vous écrivez, ou un EOF en lisant - ces conditions peuvent être détectées.

10
répondu MarkR 2008-11-12 16:15:49

sous Linux, vous pouvez installer un signal de mort parent dans l'enfant, par exemple:

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

noter que le stockage du parent process id avant la fourche et le tester dans l'enfant après prctl() élimine une condition de race entre prctl() et la sortie du processus qui a appelé l'enfant.

notez également que le signal de mort du parent de l'enfant est effacé dans les les enfants de son propre. Il n'est pas affecté par un execve() .

ce test peut être simplifié si nous sommes certains que le processus du système qui est en charge de l'adoption de tous orphelins a 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 ...

en S'appuyant sur ce processus de système étant init et ayant PID 1 n'est pas portable, cependant. POSIX.1-2008 spécifie :

le numéro de processus parent de tous les parmi les processus enfants existants et les processus Zombies du processus d'appel doit être mis à l'ID de processus d'un processus de système défini par la mise en œuvre. Qui est, ces processus doivent être hérité par un système de processus.

traditionnellement, le processus de système adoptant tous les Orphelins est PID 1, c'est - à-dire init-qui est l'ancêtre de tous les processus.

sur les systèmes modernes comme Linux ou FreeBSD un autre processus pourrait jouer ce rôle. Par exemple, sur Linux, un processus peut appeler prctl(PR_SET_CHILD_SUBREAPER, 1) pour s'établir comme un processus système qui hérite de tous les orphelins de ses descendants (cf. un exemple sur Fedora 25).

10
répondu maxschlepzig 2016-11-28 18:46:15

inspiré par une autre réponse ici, j'ai trouvé la solution ALL-POSIX suivante. L'idée générale est de créer un processus intermédiaire entre le parent et l'enfant, qui a un but: Avis quand la mère meurt, et explicitement de tuer l'enfant.

Ce type de solution est utile lorsque le code de l'enfant ne peut pas être modifié.

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

il y a deux petites mises en garde avec cette méthode:

  • Si vous tuez délibérément le processus intermédiaire, alors l'enfant ne sera pas tué quand le parent meurt.
  • si l'enfant sort avant le parent, alors le processus intermédiaire tentera de tuer le pid de l'enfant original, qui pourrait maintenant se référer à un processus différent. (Ceci pourrait être corrigé avec plus de code dans le processus intermédiaire.)

mis à part, le code que j'utilise est en Python. Ici, il est pour l'exhaustivité:

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
répondu Greg Hewgill 2014-05-01 02:25:57

Je ne crois pas qu'il soit possible de garantir qu'en utilisant seulement des appels POSIX standard. Comme dans la vraie vie, une fois qu'un enfant est engendré, il a une vie propre.

It is possible pour le processus parent d'attraper la plupart des événements de terminaison possibles, et tenter de tuer le processus enfant à ce point, mais il ya toujours certains qui ne peuvent pas être attrapés.

par exemple, aucun procédé ne peut attraper un SIGKILL . Lorsque le noyau traite ce signal il va tuer le processus spécifié sans aucune notification à ce processus que ce soit.

Pour étendre l'analogie, la seule autre façon habituelle de faire il est pour l'enfant à commettre le suicide, lorsqu'il constate qu'il n'a plus de parent.

il y a une seule façon pour Linux de le faire avec prctl(2) -voir les autres réponses.

7
répondu Alnitak 2008-11-13 08:37:26

Installer une piège gestionnaire pour attraper SIGINT, qui tue votre enfant si il est encore en vie, bien que d'autres affiches sont corrects qu'il ne prends pas de SIGKILL.

ouvert A.lockfile avec accès exclusif et avoir le sondage enfant sur elle en essayant de l'ouvrir - si l'ouverture réussit, le processus enfant devrait sortir

4
répondu Paul Betts 2008-11-12 16:42:06

comme d'autres personnes l'ont souligné, compter sur le parent pid pour devenir 1 lorsque le parent sort est non-portable. Au lieu d'attendre un ID de processus parent spécifique, il suffit d'attendre que L'ID change:

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

Ajouter un micro-sommeil comme désiré si vous ne voulez pas sonder à pleine vitesse.

cette option me semble plus simple que d'utiliser un tuyau ou de compter sur des signaux.

4
répondu user2168915 2013-05-21 00:49:22

je pense qu'un moyen rapide et sale est de créer un tuyau entre l'enfant et le parent. Quand parent sort, les enfants recevront un SIGPIPE.

3
répondu Yefei 2010-12-09 08:25:14

Cette solution a fonctionné pour moi:

  • passez la pipe stdin à l'enfant - vous n'avez pas à écrire de données dans le flux.
  • enfant lit indéfiniment de stdin jusqu'à EOF. Un EOF signale que le parent est parti.
  • c'est un moyen infaillible et portable pour détecter quand le parent est parti. Même si un parent s'écrase, OS fermera la pipe.

il s'agissait d'un procédé de type ouvrier dont l'existence n'avait de sens que lorsque le parent était vivant.

3
répondu joonas.fi 2017-10-03 06:57:22

quelques affiches ont déjà mentionné pipes et kqueue . En fait, vous pouvez également créer une paire de Unix domain sockets connectés par l'appel socketpair() . Le type de prise doit être SOCK_STREAM .

supposons que vous ayez les deux descripteurs de fichier FD1, fd2. Maintenant fork() pour créer le processus enfant, qui héritera des fds. Dans le parent, vous fermez fd2 et dans l'enfant, vous fermez fd1. Maintenant, chaque processus peut poll() le FD restant ouvert de son côté pour l'événement POLLIN . Aussi longtemps que chaque côté ne close() pas explicitement son fd pendant la durée de vie normale, vous pouvez être assez sûr qu'un drapeau POLLHUP devrait indiquer la fin de l'Autre (Peu importe propre ou non). Sur informé de cet événement, l'enfant peut décider quoi faire (par exemple, pour mourir).

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

vous pouvez essayer de compiler le code de validation de principe ci-dessus, et l'exécuter dans un terminal comme ./a.out & . Vous avez environ 100 secondes pour essayer de tuer le PID parent par différents signaux, ou il va simplement sortir. Dans les deux cas, vous devriez voir le message "enfant: parent hung up".

par rapport à la méthode utilisant SIGPIPE handler, cette méthode ne nécessite pas d'essayer l'appel write() .

cette méthode est aussi symétrique , c'est-à-dire que les processus peuvent utiliser le même canal pour surveiller l'existence de l'autre.

cette solution appelle uniquement les fonctions POSIX. J'ai essayé sous Linux et FreeBSD. Je pense que ça devrait marcher sur D'autres Unix mais je n'ai pas vraiment testé.

voir aussi:

  • unix(7) des pages de manuel Linux, unix(4) Pour FreeBSD, poll(2) , socketpair(2) , socket(7) sur Linux.
2
répondu Cong Ma 2013-03-13 05:12:35

sous POSIX , les fonctions exit() , _exit() et _Exit() sont définies comme suit:

  • si le processus est un processus de commande, le signal de SIGHUP doit être envoyé à chaque processus du groupe de processus de premier plan du terminal de commande appartenant au processus d'appel.

ainsi, si vous prenez des dispositions pour que le processus parent soit un processus de contrôle pour son groupe de processus, l'enfant il devrait y avoir un signal SIGHUP quand le parent sort. Je ne suis pas sûr que ça arrive quand le parent s'écrase, mais je pense que si. Certes, pour les non-crash cas, il devrait fonctionner correctement.

notez que vous devrez peut - être lire beaucoup de petits caractères - y compris la section des définitions de Base (définitions), ainsi que les renseignements sur les services du système pour exit() et setsid() et setpgrp() - pour obtenir une vue d'ensemble. (Donc, je voudrais!)

1
répondu Jonathan Leffler 2008-12-07 15:35:11

si vous envoyez un signal au pid 0, en utilisant par exemple

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

ce signal est envoyé à l'ensemble du groupe de traitement, tuant ainsi effectivement l'enfant.

vous pouvez le tester facilement avec quelque chose comme:

(cat && kill 0) | python

si vous appuyez ensuite sur ^D, vous verrez le texte "Terminated" comme une indication que L'interpréteur Python a bien été tué, au lieu de simplement quitté à cause de stdin étant fermé.

1
répondu Thorbiörn Fritzon 2011-12-05 21:31:47

dans le cas où cela concerne quelqu'un d'autre, lorsque je lance des instances JVM dans des processus forked child à partir de C++, la seule façon pour moi d'obtenir que les instances JVM se terminent correctement après que le processus parent a été terminé était de faire ce qui suit. J'espère que quelqu'un pourra fournir des commentaires dans les commentaires si ce n'était pas la meilleure façon de le faire.

1) appeler prctl(PR_SET_PDEATHSIG, SIGHUP) sur le processus forked child comme suggéré avant de lancer L'application Java via execv , et

2) Ajouter un crochet d'arrêt à L'application Java qui polls jusqu'à ce que son PID parent égale 1, puis faire un dur Runtime.getRuntime().halt(0) . Le sondage se fait en lançant un shell séparé qui exécute la commande ps (voir: Comment trouver mon PID en Java ou JRuby sur Linux? ).

MODIFIER 130118:

Il semble que ce n'était pas une solution robuste. Je lutte encore un peu pour comprendre les nuances de ce qui se passe, mais je recevais encore parfois des processus JVM orphelins lors de l'exécution de ces applications dans des sessions screen/SSH.

au lieu de rechercher le PPID dans L'application Java, j'ai simplement demandé au hook shutdown d'effectuer un nettoyage suivi d'un arrêt brutal comme ci-dessus. Puis j'ai fait en sorte d'invoquer waitpid dans L'application c++ parent sur le processus d'enfant engendré quand il était temps de tout arrêter. Cela semble être une solution plus robuste, car le processus enfant assure qu'il se termine, tandis que le parent utilise des références existantes pour s'assurer que ses enfants se terminent. Comparez cela à la solution précédente qui avait le processus parent se termine chaque fois qu'il lui plaît, et avait les enfants essayer de comprendre si ils avaient été orphelins avant de se terminer.

1
répondu jasterm007 2017-05-23 12:10:32

bien que 7 ans se soient écoulés, je viens de rencontrer ce problème car J'exécute L'application SpringBoot qui doit démarrer webpack-dev-server pendant le développement et doit le tuer lorsque le processus d'arrière-plan s'arrête.

j'essaie d'utiliser Runtime.getRuntime().addShutdownHook , mais il a travaillé sur Windows 10, mais pas sur Windows 7.

Je l'ai changé pour utiliser un thread dédié qui attend que le processus cesse ou pour InterruptedException qui semble fonctionner correctement sur les deux fenêtres version.

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
répondu Ido Ran 2016-10-25 11:24:00

si le parent décède, le code PPID des orphelins passe à 1 - vous n'avez qu'à vérifier votre propre code PPID. Dans un sens, c'est l'interrogation, mentionné ci-dessus. voici shell pièce pour cela:

check_parent () {
      parent=`ps -f|awk '=='$PID'{print  }'`
      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
répondu alex K 2011-12-06 23:17:22

j'ai trouvé 2 solutions, toutes les deux pas parfaites.

1.Tuer tous les enfants en tuant (- pid) lorsqu'ils reçoivent le signal SIGTERM.

Évidemment, cette solution ne peut pas gérer "kill -9", mais il ne fonctionne pour la plupart des cas et très simple parce qu'il n'a pas besoin de se souvenir de tous les processus enfant.


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

de la même manière, vous pouvez installer 'exit' handler comme ci-dessus si vous appelez process.sortie quelque part. Remarque: les touches Ctrl+C et accident soudain ont été automatiquement traitées par OS pour tuer le groupe de processus, donc plus ici.

2.Utilisez chjj / pty.js pour lancer votre processus avec terminal de contrôle attaché.

Quand vous tuez le processus courant de toute façon même kill -9, tous les processus enfants seront automatiquement tués aussi (par OS?). Je suppose que parce que le processus courant tient un autre côté du terminal, donc si le processus courant meurt, le processus enfant va obtenir SIGPIPE donc meurt.


    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
répondu osexp2003 2015-03-24 04:12:17

j'ai réussi à faire une solution portable, non-polling avec 3 processus en abusant du contrôle terminal et des sessions. C'est de la masturbation mentale, mais ça marche.

l'astuce est:

  • le processus A est lancé
  • Un processus crée un tuyau P (et ne lit jamais)
  • processus de fourchettes dans le processus B
  • le processus B crée une nouvelle session
  • procédé B attribue un terminal virtuel pour cette nouvelle session
  • processus B installe SIGCHLD gestionnaire de mourir lorsque l'enfant quitte le
  • processus B définit un SIGNAL handler
  • fourches du procédé B dans le procédé c
  • le processus c fait tout ce dont il a besoin (par ex. exec () s le binaire non modifié ou exécute n'importe quelle logique)
  • process B écrit à la pipe P (et bloque de cette façon)
  • processus Un wait()s sur procédé B et sort lorsqu'il meurt

par là:

  • si le processus de matrices: processus B reçoit un SIGNAL et meurt
  • si le processus B meurt: process a's wait () retourne et meurt, le processus C obtient un SIGHUP (parce que lorsque le chef de session d'une session avec un terminal attaché meurt, tous les processus dans le groupe de processus de premier plan obtiennent un SIGHUP)
  • si le processus c meurt: le processus B obtient un SIGCHLD et meurt, ainsi, le processus de fabrication est

lacunes:

  • processus de C ne peut pas gérer SIGHUP
  • processus de C sera exécuté dans une session différente
  • le processus C ne peut pas utiliser l'API session/process group parce qu'il va casser la configuration fragile
  • la création d'un terminal pour chaque opération de ce type n'est pas la meilleure idée jamais
0
répondu Marek Dopiera 2016-08-21 16:22:06

historiquement, à partir D'UNIX v7, le système de processus a détecté l'orphanité des processus en vérifiant l'id parent d'un processus. Comme je l'ai dit, historiquement, le processus du système init(8) est un processus spécial pour une seule raison: il ne peut pas mourir. Il ne peut pas mourir parce que l'algorithme du noyau pour gérer l'assignation d'un nouvel identifiant de processus parent, dépend de ce fait. lorsqu'un processus s'exécute son exit(2) appel (par le biais d'un système d'appel externe ou de la tâche que l'envoi d'un signal ou) le noyau réassigne à tous les enfants de ce processus l'id du processus init comme id du processus parent. Cela conduit au test le plus facile, et la façon la plus portable de savoir si un processus est devenu orphelin. Il suffit de vérifier le résultat de la getppid(2) appel système et si c'est l'id de processus du init(2) processus, puis le processus s'orphelin avant l'appel système.

deux questions émergent de cette approche qui peuvent conduire à des questions:

  • tout d'abord, nous avons la possibilité de changer le processus init à n'importe quel processus utilisateur, alors comment pouvons-nous assurer que le processus init sera toujours parent de tous les processus orphelins? Eh bien, dans le code d'appel système exit il y a une vérification explicite pour voir si le processus exécutant l'appel est le processus init (le processus avec pid égal à 1) et si c'est le cas, le noyau panique (il ne devrait plus pouvoir maintenir la hiérarchie du processus) donc il n'est pas autorisé pour le processus en cours pour faire un appel exit(2) .
  • deuxièmement, il y a une condition de course dans le test de base exposé ci-dessus. L'id du processus d'Init est supposé historiquement être 1 , mais ce n'est pas justifié par L'approche POSIX, qui stipule (comme exposé dans d'autres réponses) que seul l'id du processus d'un système est réservé à cette fin. Presque aucune implémentation posix ne fait cela, et vous pouvez supposer dans les systèmes dérivés unix originaux que d'avoir 1 comme réponse getppid(2) appel système est suffisant pour assumer le processus est orphelin. Une autre façon de vérifier est de faire un getppid(2) juste après la fourche et de comparer cette valeur avec le résultat d'un nouvel appel. Cela ne fonctionne tout simplement pas dans tous les cas, car les deux appels ne sont pas atomiques ensemble, et le processus parent peut mourir après le fork(2) et avant le premier getppid(2) appel système. Le processus parent id only changes once, when its parent does an exit(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)", mais vous pouvez supposer en toute sécurité ces processus comme n'ayant aucun parent non plus (sauf si vous remplacez dans un système le processus init)
0
répondu Luis Colorado 2017-03-08 08:59:54

une autre façon de faire cela qui est spécifique à Linux est de faire en sorte que le parent soit créé dans un nouvel espace de noms PID. Il sera alors PID 1 dans cet espace de nom, et quand il sort, tous ses enfants seront immédiatement tués avec SIGKILL .

malheureusement, pour créer un nouvel espace de noms PID, vous devez avoir CAP_SYS_ADMIN . Mais cette méthode est très efficace et ne nécessite aucun changement réel pour le parent ou les enfants au-delà du lancement initial du parent.

Voir clone(2) , pid_namespaces(7) , et départager(2) .

0
répondu Omnifarious 2017-12-06 18:20:55