fork() und Kindprozess killen+neustart



  • Ich habe mittels fork() einen Kindprozess erzeugt. Der Vaterprozess soll diesen auf Absturz überwachen und gegebenenfalls killen und neu starten.

    So habe ich es mir bis hierin gedacht:

    # include <stdio.h>
    # include <curses.h>
    # include <signal.h>
    # include <ctype.h>
    # include <string.h>
    # include <stdlib.h>
    # include <unistd.h>
    # include <sys/types.h>
    # include <sys/wait.h>
    # include <errno.h>
    
    int main(int argc, char *argv[])
    {
      int vater_pid;
      int absturz = 0;
    
      pid_t kind_pid;
    
      switch (kind_pid = fork()) {
        case -1: perror("\n fork()");
                 return EXIT_FAILURE;
        case 0:
                 /* Der Kindprozess. In diesem Falle xterm zum Testen */
                 execlp("xterm", NULL, NULL);
                 break;
        default:
                 /* Vaterprozess */
                 waitpid(kind_pid, NULL, WNOHANG);
                 vater_pid=getpid();
                 while (1) {
                   absturz = pruefe_absturz();
                   switch (absturz) {
                     case 1:
                       printf("Absturz\n");
                       kill(kind_pid, 15);
                       break;
                     default:
                       printf("alles in Ordnung\n");
                       break;
                   }
    }
    
    int absturz()
    { blablabla }
    

    Ich will nun, dass nicht nur das Kind gekillt wird, sonder ein neues auch entsteht. Dabei soll die kind_pid natürlich ebenfalls aktualisiert werden, damit im Falle eines Falles das Kind wieder gekillt werden kann.

    Ich weiß... ein hoher verbrauch an kleinwüchsigen Rotznasen 😃



  • du hast fast richtig.

    default:
      int child_status;
      waitpid(kind_pid, &child_status, 0); /* 0 statt WNOHANG */
      if(WIFEXITED(child_status))
          printf("alles in Ordnung\n");
      else
          printf("Absturz\n");
    

    siehe man: wiatpid. Die W* Macros sind das, was du suchst.

    btw: falsches Forum, hat mit ANSI C nichts zu tun.



  • Die Sache mit dem WNOHANG ist schon richtig. Der Elternprozess soll gleichzeitig mit dem Kinderprozess laufen. Ich überprüfe den Absturz anhand einer Logdatei, dessen Inhalt immer geändert wird. Wird diese nicht mehr geändert, ist der Kinderprozess abgeschmiert. So kann ich über einen einfachen Stringvergleich den Absturz feststellen.

    Ich habe es jetzt geschafft, dieses abgestürzte Programm immer wieder Neustarten zu lassen, in dem ich es in eine andere Funktion ausgelagert habe. Die Main ruft diese in einer while(status != 1) Schleife ab. Bei Betreten der Schleife wird status auf 1 gesetzt damit ich einen Loop verhindern kann. Sollte der Kindprozess beendet werden, so gibt dieser 0 zurück. Damit würde der Prozess auf ein neues aufgerufen werden usw... So weit so gut. Der Kind-prozess wird gekillt und neu gestartet.

    Und nun mein Phänomeniales Problem: ich kill den Kinderprozess mittels

    kill (kind_pid, 15);
    return 0;
    

    Jetzt sollte der Kindprozess gekillt werden, und das Programm zurück zu main springen. Von da wieder auf ein neues Starten. Den wiederstart des Vaters umgehe ich mit einem erster_durchgang=1... Der Kindprozess startet tatsächlich neu.

    Das Problem: Der Kindprozess wird in der Prozesstabelle als Zombie geführt. Selbst ein manuelles kill -15 oder kill -9 hilft da nicht weiter. Der Zombie bleibt bestehen. Wenn der Vaterprozess gekillt wird, so killt init die Zombieprozesse. Was init macht, will ich aber nun in meinem Vaterprozess machen. Wie stell ich das am besten an?

    Schonmal vielen Danke für Eure Hilfen...



  • gnome.gemini schrieb:

    Die Sache mit dem WNOHANG ist schon richtig. Der Elternprozess soll gleichzeitig mit dem Kinderprozess laufen.

    ich hab nur das Verhalten deines Beispieles angenommen. Dort machst du while(1) und überprüfst, dass das Kind nicht abgestürzt ist. Da kann waitpid auch ruhig blockieren, denn in deiner while(1) machst du ehe nichts "vernünftiges".

    gnome.gemini schrieb:

    Ich habe es jetzt geschafft, dieses abgestürzte Programm immer wieder Neustarten zu lassen, in dem ich es in eine andere Funktion ausgelagert habe. Die Main ruft diese in einer while(status != 1) Schleife ab.

    Verwende die W* Marcos. In 'status' befindet sich mehr Information als nur der Exit-Status des Kindes.

    Du könntest sowas machen

    while(1)
    {
      pid = fork();
    
      if(pid < 0)
         FEHLER; 
    
      if(pid == 0)
      {
        /* Kind Prozess läuft */
      }
    
      waitpid();
    
      wenn Kind nicht gecrasht sondern sauber beendet
        break;
    }
    

    damit sollte dein Kind neustarten, wenn es crasht.

    Um Zombies zu vermeiden, muss man folgendes tun:

    pid_t child, realchild;
    
    child = fork();
    
    if(child < 0)
      FEHLER;
    
    if(child == 0)
    {
      child = fork();
    
      if(child < 0)
        exit(1);
    
      if(child == 0)
      {
         /* Kind vom Kind (A): hier läuft dein Programm */
      }
    
      exit(child); /* damit init (A) als Vater übernimmt und 
        liefert pid vom Enkel als exit status. "böser" hack */
    }
    
    waitpid(child, &staus, 0);
    realchild = WEXITSTATUS(status);
    


  • Dieser Thread wurde von Moderator/in rüdiger aus dem Forum ANSI C in das Forum Linux/Unix verschoben.

    Im Zweifelsfall bitte auch folgende Hinweise beachten:
    C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?

    Dieses Posting wurde automatisch erzeugt.



  • supertux schrieb:

    ich hab nur das Verhalten deines Beispieles angenommen. Dort machst du while(1) und überprüfst, dass das Kind nicht abgestürzt ist. Da kann waitpid auch ruhig blockieren, denn in deiner while(1) machst du ehe nichts "vernünftiges".

    Doch, ich rufe die Funktion pruefe_absturz() auf, welche dann die Logdatei öffnet, den Inhalt in einen herauskopiert, 5s wartet, erneut den Inhalt in einen anderen String kopiert, die beiden strings miteinander vergleicht, und im Falle einer gleichheit, nochmal 10s pause gönnt, bevor er mit dem Wert 0 zurückkommt und das Kind gekillt wird. Deswegen darf er nicht blockieren.



  • gnome.gemini schrieb:

    supertux schrieb:

    ich hab nur das Verhalten deines Beispieles angenommen. Dort machst du while(1) und überprüfst, dass das Kind nicht abgestürzt ist. Da kann waitpid auch ruhig blockieren, denn in deiner while(1) machst du ehe nichts "vernünftiges".

    Doch, ich rufe die Funktion pruefe_absturz() auf, welche dann die Logdatei öffnet, den Inhalt in einen herauskopiert, 5s wartet, erneut den Inhalt in einen anderen String kopiert, die beiden strings miteinander vergleicht, und im Falle einer gleichheit, nochmal 10s pause gönnt, bevor er mit dem Wert 0 zurückkommt und das Kind gekillt wird. Deswegen darf er nicht blockieren.

    ok, aber *das* könnte ich aus " blablabla " Code nicht herauslesen und wenn die Funktion pruefe_absturz heißt, dann ging ich davon aus, dass sie nur prüft, ob der Prozess abgestürzt ist oder nicht. Wie dem auch sei, jetzt weiß ich warum die WNOHANG brauchst.

    Wenn ein Kindprozess beendet, dann wird das SIGCHLD Siganl ausgelöst. Standardmäßig wird dieses Siganl ignoriert. Dein Vaterprozess muss also einen Sighandler für SIGCHLD. Siehe man: signal(7)

    edit: meine waiptpid-manpage hat ein schönes Bsp, vielleicht hlft dir das weiter:

    The following shell session demonstrates the use of the program:

    $ ./a.out &
    Child PID is 32360
    [1] 32359
    $ kill -STOP 32360
    stopped by signal 19
    $ kill -CONT 32360
    continued
    $ kill -TERM 32360
    killed by signal 15
    [1]+  Done                    ./a.out
    $
    
    #include <sys/wait.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <stdio.h>
    
    int
    main(int argc, char *argv[])
    {
       pid_t cpid, w;
       int status;
    
       cpid = fork();
       if (cpid == -1) {
           perror("fork");
           exit(EXIT_FAILURE);
       }
    
       if (cpid == 0) {            /* Code executed by child */
           printf("Child PID is %ld\n", (long) getpid());
           if (argc == 1)
               pause();                    /* Wait for signals */
           _exit(atoi(argv[1]));
    
       } else {                    /* Code executed by parent */
           do {
               w = waitpid(cpid, &status, WUNTRACED | WCONTINUED);
               if (w == -1) {
                   perror("waitpid");
                   exit(EXIT_FAILURE);
               }
    
               if (WIFEXITED(status)) {
                   printf("exited, status=%d\n", WEXITSTATUS(status));
               } else if (WIFSIGNALED(status)) {
                   printf("killed by signal %d\n", WTERMSIG(status));
               } else if (WIFSTOPPED(status)) {
                   printf("stopped by signal %d\n", WSTOPSIG(status));
               } else if (WIFCONTINUED(status)) {
                   printf("continued\n");
               }
           } while (!WIFEXITED(status) && !WIFSIGNALED(status));
           exit(EXIT_SUCCESS);
       }
    }
    


  • Vielen Dank. Das war es!



  • supertux schrieb:

    Um Zombies zu vermeiden, muss man folgendes tun:

    pid_t child, realchild;
    
    child = fork();
    
    if(child < 0)
      FEHLER;
    
    if(child == 0)
    {
      child = fork();
    
      if(child < 0)
        exit(1);
    
      if(child == 0)
      {
         /* Kind vom Kind (A): hier läuft dein Programm */
      }
      
      exit(child); /* damit init (A) als Vater übernimmt und 
        liefert pid vom Enkel als exit status. "böser" hack */
    }
    
    waitpid(child, &staus, 0);
    realchild = WEXITSTATUS(status);
    

    spät, aber doch...

    "exit(child)" ist aber ein wahrhaft "böser" hack 😉
    er wird nur leider nicht funktionieren. exit akzeptiert nur Werte von 0 bis 255 bzw. nimmt exit den übergebenen wert einfach modulo 256, wenn ich mich richtig erinnere. hier ist ein link der das in etwa bestätigt (also vom ergebnis zumindest; ob dabei modulo gerechnet wird, steht aber nicht dabei):
    http://www.cs.utah.edu/dept/old/texinfo/glibc-manual-0.02/library_22.html#SEC397


Anmelden zum Antworten