exec und rückmeldung im parent ohne zombie



  • frank schrieb:

    ich habe doch für beide Forks den else-zweig...was ist denn da falsch?

    mhm...könnte natürlich sein, dass dieser nie erreicht wird...

    Das meinte ich^^

    frank schrieb:

    wie muss func aussehen? muss ja sicher ein bestimmtes format haben

    z.B. so:

    void func() {
    	pid_t pid;
    	int status;
    	while( (pid=waitpid(-1, &status, WNOHANG)) > 0 ) {
    		// mache was
    	}
    }
    

    waitpid wartet auf Prozesse.
    -1: Irgendein Kinder
    status: enthält danach Infos über Kind(z.B. wie es beendet wurde)
    WNOHANG: wenn kein Zombie gefunden wurde kehr waitpid sofort zurückund liefert -1 (ansonsten wartet es bis sich einer beendet)
    pid: ID des gefundenen Zombies

    Ich weiß nicht, ob du nur ein Kind erzeugst, aber bei mehreren bietet sich so eine while Schleife an.
    In der Funktion kannst du eigentlich fast alles machen. Du musst nur aufpassen, weil der Signalhandler deinen Vater irgendwo unterbricht, was zu problemen führen kann, wenn du auf gleichen Daten arbeitest. (sigprocmask() falls du das Problem hast). unter "man 7 signal" findest du eine Liste mit Funktionen, die du sicher in func verwenden kannst. Bei anderen kann es zu Problemen führen.

    frank schrieb:

    sind pipes "Echtzeit"?
    also kann ich in der gleichen fork-verzweigung im child-teil schreiben und im parent-teil lesen?

    Zu Pipes kann ich dir momentan noch eigentlich nichts sagen, weil ich sie noch nie verwendet habe, aber anscheinend funktionieren sie so Dateien. Das Beispiel ist glaube ich genau was du willst^^:
    http://linux.die.net/man/2/pipe
    Da kannst du natürlich ein write und read machen und das read wird solange blockieren, bis was da ist (mit Echtzeit hat das aber nichts zu tun)

    Mfg
    DerBaer



  • DerBaer schrieb:

    z.B. so:

    void func() {
    	pid_t pid;
    	int status;
    	while( (pid=waitpid(-1, &status, WNOHANG)) > 0 ) {
    		// mache was
    	}
    }
    

    ...
    Ich weiß nicht, ob du nur ein Kind erzeugst, ...
    In der Funktion kannst du eigentlich fast alles machen. Du musst nur aufpassen, weil der Signalhandler deinen Vater irgendwo unterbricht, was zu problemen führen kann, wenn du auf gleichen Daten arbeitest. (sigprocmask() falls du das Problem hast). unter "man 7 signal" findest du eine Liste mit Funktionen, die du sicher in func verwenden kannst. Bei anderen kann es zu Problemen führen.

    Hi Baer,
    ich erzeuge nur ein child (media-player).Wenn ich das richtig interpretiere (mit der while-schleife) würde ich mit der func vermutlich den gleichen Effekt haben, wie mit system...die Grafische Oberfläche (GTK) wird nicht mehr gezeichnet und navigieren darauf wird unmöglich.
    ich nehme ja fork/exec (bzw. will das), um mein Programm von dem Player zu entkoppeln.
    als Status im parent bräuchte ich eigentlich nur:
    - konnte programm gestartet werden
    - wurde programm beendet
    ob ich das per pipes oder anders realisiere ist mir eigentlich egal...sollte halt möglichst einfach sein 🙂
    evtl. geht das auch mit signals einfacher...dazu müsste der child-prozess nur den parent-prozess kennen, oder?

    Gruß Frank



  • OK sry, hab das exec vergessen. Dein Prozess kann nicht sagen, wenn es geklappt hat (wurde ja ersetzt durch exec). Du kannst nur mitteilen, falls es nicht geklappt hat. Da du darauf aber nicht warten kann(weil ja möglichcerweise nichts kommt), sind pipes vielleicht doch nicht so geeignet.

    Aber es könnte so gehen:
    Du machst den Signalhandler für SIGCHLD, der ein waitpid(-1, &status, WNOHANG) aufruft (braucht keine Schleife bei einem Kind).
    Und dann untersuchst du den Statuscode.

    Also:
    V: Vater, K:Kind
    V: installiere Signalhandler
    V: fork()
    V: arbeite normal weiter K: mache ein exec
    V: arbeite ... K: falls erfolgreich passiert nix(dein Programmcode ist ja weg)
    V: arbeite ... K: falls Fehler: exit(237)
    V: Arbeit unterbrochen von func K: Zombie
    V: waitpid(-1, &status, WNOHANG) K: Zombie verschwindet
    V: teste Staus mit WIFEXITED(status)
    V: falls ja: teste ob WEXITSTATUS(status) = 237
    wobei du
    - keine Benachrichtigung kriegst, falls es erfolgreich war
    - nicht weißt, wo der Fehler aufgetreten ist (wenn exec klappt aber das neue Programm sich zufällig mit z.B. 237 als code beendet siehst du den Unterschied nicht.

    Und die Signalhandler warten nur auf Signale, blockieren aber den Ablauf nicht, sodass die GUI trotzdem laufen sollte.

    frank schrieb:

    Wenn ich das richtig interpretiere (mit der while-schleife) würde ich mit der func vermutlich den gleichen Effekt haben, wie mit system...die Grafische Oberfläche (GTK) wird nicht mehr gezeichnet und navigieren darauf wird unmöglich.

    du rufst func selbst nicht auf. Das macht das betriebsystem für dich und zwar sobald ein Prozess beendet wurde. Dann geht die Schleife her, rattert einmal über alle Prozesse, deren Status sich geändert hat drüber,(bei dir genau dieser eine) entsorgt diesen Zombie und wird wegen dem WNOHANG danach gleich 0 zurückliefern und die func wieder verlassen. Da sollte nichts blockieren.



  • Hallo,
    ich hab jetzt so, und es funktioniert super, soweit ich es nach bisherigen Tests beurteilen kann

    void child_callback(int param)
    {
      pid_t pid;
      int status;
      pid=waitpid(-1, &status, WNOHANG);
      g_print("child terminated (%d)\n",status);
      if (status>0)
        ShowError("Fehler bei der Ausführung!");
    }
    
    bool Exec(string command,string parameters,string file)
    {
      bool ret=true;
      //arg-array bauen
    
      pid_t pid=fork();  
      if (pid==-1)
      {
        g_print("problem while forking\n");//return false; //problem while forking
        ret=false;
      }else if (pid==0)//child-process
      {
        if(execvp(command.c_str(), args) == -1) 
        {
          perror("execvp");
          _Exit(EXIT_FAILURE);//errors in child are handled by signal-handler
        }
      } else 
      {
        //parent process
        struct sigaction sa;
        sigemptyset(&sa.sa_mask);
        sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
        sa.sa_handler = child_callback;
        sigaction(SIGCHLD, &sa, NULL); 
      }
      return ret;
    }
    

    Danke Baer 🙂

    Gruß Frank



  • Hi,

    glück gehabt 😉
    Prinzipiell alles richtig, nur musst du den Signal-Handler nicht im Vaterprozess anlegen, sondern noch vor dem fork();
    Problem sonst: fork() -> Kind wird als erstes ausgeführt -> terminiert
    und erst dann kommt der Vater wieder dran und darf weiterrechnen -> installiert Handler, aber Signal kam ja schon vorher = verloren
    Wenn du vor dem fork den Handler installierst, hat ihn der Vater schon und wenn dann das Kind zuerst rechnen darf und sich gleich beendet wird das erkannt.

    P.S fork() übernimmt die eingestellten Handler(heißt Kind hat den auch installiert, wobei es kein eigenes Kind hat, also nutzlos^^), aber beim exec wird der SIgnalhandler dann ausm Kind gelöscht.



  • wenn ich den signal-handler-block vor dem fork platziere, hängt der parent 😢



  • hm. da bin ich überfragt 😕 . Bei mir hat das immer tadellos funktioniert.
    Ich werde das demnächst mal bei mir testen.

    int Exec(char * command, char ** parameters, char * file)
    {
      int ret=1;
      //arg-array bauen
    
    	struct sigaction sa;
    	sigemptyset(&sa.sa_mask);
    	sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
    	sa.sa_handler = child_callback;
    	sigaction(SIGCHLD, &sa, NULL);
    
      pid_t pid=fork();  
      if (pid==-1)
      {
        g_print("problem while forking\n");//return false; //problem while forking
        ret=false;
      }else if (pid==0)//child-process
      {
        execlp(command, NULL);
        perror("execvp");
        _Exit(EXIT_FAILURE);//errors in child are handled by signal-handler
      } else
      {
    	//parent process
    	/*int i = 0;
    	for (;i < 100000000; i++) {
    		printf("%c", 0);
    	}*/
      }
    	printf("Vater fertig\n");
      return ret;
    }
    

    So funktioniert das bei mir.
    - Die Schleife mit printf() habe ich testhalber drinnen gehabt um zu simulieren, dass das Kind zuerst terminiert bevor der Signalhandler installiert werden konnte (->< Signal war verloren).
    - Nachdem ich den Signal-Block nach vorne geschoben habe, lief es mit und ohne Schleife
    - Ich hab die If-Abfrage bei execvp (habs tsethalber in execlp geändert) weggelassen, da execvp sowieso immer -1 zurückliefert (im Erfolgsfall ist dein Programmcode ja weg)

    Hast du in der call_back oder in der Exec noch Code der unerwünschte Nebeneffekte haben könnte? Oder wo genau hängt der Vater?

    Tut mir leid, dass das immer noch nicht ganz richtig läuft.



  • habe meine komplette callback geposted...das einzige "komplexe" ist ShowError,
    welches den Text in einer GtkInfoBar (im parent) anzeigt (wär hier vermutlich bisschen viel zum posten [in Klasse eingebetter])

    die zeile mit dem perror wird trotzdem nicht erreicht (nur im fehlerfall)?

    der parent hängt nachdem das child beendet wurde:

    letzte Ausgabe:
    execvp: No such file or directory
    child terminated (256)

    der child-prozess ist bei "ps auxf" nicht (mehr) sichtbar



  • frank schrieb:

    letzte Ausgabe:
    execvp: No such file or directory
    child terminated (256)

    der child-prozess ist bei "ps auxf" nicht (mehr) sichtbar

    das heißt immerhin, dass das Signal richtig abgefangen wird und waitpid() den Zombie richtig aufräumt.
    Ich denke du solltest nicht status verwenden zum Anzeigen,sondern WEXITSTATUS(status). Da stecken mehrere Daten drinnen als der reine Exitcode, aber das sind erstmal Details.

    Das mit der komplexen ShowMessage könnte möglicherweise das Problem sein, weil nicht alle Funktionen sicher vor überlagerung sind, also wenn dein Vater die Funktion aufruft und mittendrin durch das Signal unterbrochen wird und der die gleiche Funktion aufruft.
    Wenn du die ShowMessage auskommentierst, sollte es eigentlich laufen, oder?
    Wenn das das Problem ist, fällt mir jetzt nur ein, diese problematische Funktion möglichst zu ersetzten, oder im Vaater vor jedem Aufruf das SIGCHLD temporär zu blockieren.



  • mhm..scheint an der error-funktion zu liegen...

    diese sieht erstmal so aus:

    void ShowError(string message)
    {
       g_print("Error: %s\n",message.c_str());
       message="Fehler: "+message;
       Mainform.InfoBar->ShowBar(GTK_MESSAGE_ERROR,message.c_str(),true);
    }
    

    die erste zeile ist ja ein g_print...welches aber scheinbar schon nicht ausgeführt wird...
    selbst wenn ich die beiden letzten Zeilen auskommentiere, hängt sich der parent auf...warum ist mir unklar, da ja auch nur ein g_print drin ist...

    meine callback sieht jetzt so aus:

    void child_callback(int param)
    {
      pid_t pid;
      int status;
      while( (pid=waitpid(-1, &status, WNOHANG)) > 0 ) 
      {
        int exitcode=WEXITSTATUS(status);
        g_print("child terminated (%d)\n",exitcode);
        if (exitcode>0)
          g_print("Error in child...\n");
          //ShowError("Wiedergabeprogramm konnte nicht gestartet werden!");
      } 
    }
    


  • also so wie ich das sehe funktioniert das erste g_print(), aber das zweite nicht?

    Ich kannte die Funktion g_print() leider nicht, aber vielleicht hat sie einen Puffer der nicht automatisch bei '\n' geleert wird oder hat sonstige Probleme, die nichts mit dem Signalhandler zu tun haben?
    Interessant fände ich auch mal, was in errorcode drinsteht, denn die "least significant 8 bits" wie in der Man-page steht ergeben 0 wenn der statuscode 256 ist.
    Funktioniert es, wenn du z.B. ein Printf() an die stelle machst?

    Das sind jetzt nur noch wage Vermutungen, weil es langsam absolut keinen Sinn mehr ergibt.
    Tut mir echt leid, dass ich da keine Idee mehr habe...



  • Die ersten beiden g_print in der child_callback funktionieren.nur das g_print in der showerror nicht. Der exitcode ist da ubrigends 1 (exit_failure)



  • ich habe den code mal isoliert in ein separetes Programm kopiert, da funktioniert er...irgendwas blockiert im Haupt-programm scheinbar...aber keine Ahnung, wo und was...

    ich könnte dir zwar den kompletten Code geben, aber ich denke, es ist zuviel verlangt, dass du mein Programm debuggst 😞

    Gruß Frank


Anmelden zum Antworten