Ersetzten von system() durch execv, mit Umleiten der Ausgabe.



  • Hallo,

    Ich erzeuge aus einem Satz an Messdaten mit gnuplot eine große Anzahl an PNG Dateien. Diese sollen mit mencoder zusammengefügt werden. Das funktioniert einwandfrei solange ich

    system("mencoder mf://plots/*.png -mf fps=25:type=png -ovc copy -oac copy -o spiral-25-fps.avi > /dev/null 2>&1");
    

    verwende. Ich würde jedoch gern execv (oder einen anderen Vertreter der exec Familie) nutzen. Leider scheitere ich am Umleiten der Ausgabe ("> /dev/null 2>&1"). Auch ein Aufruf mit "bash -c" gefolgt vom mencoder-Aufruf als String ist fehlgeschlagen, da nCurses verwendet wird und es so zu Darstellungsfehlern kommt.

    Hat jemand eine Idee der ich nachgehen kann?

    Vielen Dank für eure Zeit.



  • Hi, hast du es schon mit einer händischen Implementierung von Pipes probiert, um ">" zu umgehen?



  • ...hast du ... schon ... Pipes probiert...

    Ja, das ist sicher eine Möglichkeit. Ich verwende Pipes z.B. um gnuplot anzusprechen

    FILE *pipe = popen("gnuplot > /dev/null 2>&1", "w");
    ...
    fprintf(pipe, "set terminal png size 600,600 nocrop transparent\n");
    ...
    

    Eigentlich ist das Ersetzten von system() auch mehr eigener Anspruch bzw. Umsetzten von "best practice" im Generellen. Ich habe an verschiedenen Stellen gelesen, dass system() wohl eher eine Lösung zum abgewöhnen ist und execl() (ö.a.) ein sicheres Pendant. Würdest du in diesem Fall Pipes verwenden oder eine ganz andere Lösung?



  • Ich persönlich würde Pipes nehmen.

    Zur IPC fällt in deinem Fall ja auch schon einiges weg(shared Memory etc.).

    Bei Pipes hast du allerdings ne maximale Buffergröße(bei mir glaube 64KB) 😉



  • Es gibt zwischen system und exec* einfach einen gravierenden Unterschied: Bei system wird Dein Prozess pausiert und erst dann fortgesetzt, wenn der Unterprozess sich beendet hat. Bei exec* wird Dein Prozess durch einen neuen ersetzt, damit ist Dein Programm automatisch beendet, wenn der neue Prozess beendet ist. Der Vollständigkeit halber startet popen einen Unterprozess und kehrt danach zu Deinem Prozess zurück, d.h. beide Prozesse laufen parallel.

    Zu sagen, system wäre zum abgewöhnen, halte ich für zu pauschal. Wenn es die Anforderungen erfüllt, kann man es auch nehmen. Man hat dabei natürlich keine Kontrolle über den Unterprozess.

    Vereinfacht macht system folgendes (plus Fehlerbehandlung):

    int system(char const* cmd)
    {
        int status;
        pid_t pid = fork();
        if (pid == 0) {
            execl("/bin/sh", "sh", "-c", cmd, NULL);
        }
        wait(&status);
        return status;
    }
    

    D.h. garnicht so viel besonderes. Wenn Du system meiden willst, damit Du den Unterprozess besser kontrollieren kannst, kannst Du das adaptieren und statt wait irgendwas anderes machen.

    Ein Umleiten von stdout und stderr würde übrigens folgendem entsprechen:

    close(1);
        open("/dev/null", O_WRONLY);
        dup2(1, 2);
        execl(...);
    


  • open("/dev/null", O_WRONLY);
    dup2(1, 2);
    
    dup2(1, open("/dev/null", O_WRONLY)); // Ist das so nicht sinnvoller? 2 ist bei mir immernoch auf stderr
    


  • DaRe schrieb:

    dup2(1, open("/dev/null", O_WRONLY)); // Ist das so nicht sinnvoller? 2 ist bei mir immernoch auf stderr
    

    Wenn, dann:

    dup2(open("/dev/null", O_WRONLY), 2);
    

    dup2 schließt den zweiten Filedescriptor und dupliziert den ersten auf den zweiten.



  • Danke für die Antworten. Der betreffende mencoder-Aufruf befindet sich in einem eignem Thread. So funktioniert das ganze ohne Probleme und ist gleichzeitig um längen schneller als ein direkter Aufruf von der shell oder system(). Weiß zufällig jemand warum das so ist? Eine weitere Frage hätte ich noch. Das ganze funktioniert wirklich nur wenn innerhalb des Threads ein neuer Prozess mit fork() erfolgt. Sonst endet wie von LordJaxom beschrieben das Programm an sich. Warum wird nicht nur der Thread beendet, wenn kein fork() erfolgt?

    pthread_t thread1, ...;
    ...
    pthread_create( &thread1, NULL, call_mencoder, NULL);
    ...
    void *call_mencoder( void *ptr )
    {
    ...
    	pid_t pid = fork();
        if (pid == 0) {
    		close(1);
    		dup2(open("/dev/null", O_WRONLY), 2);
    		execl("/usr/bin/mencoder", "mencoder", "mf://plots/*.png","-mf", "fps=25:type=png", "-ovc", "copy", "-oac", "copy", "-o", "spiral-25-fps.avi", NULL);
        } 
    ...
    }
    


  • Weil exec nunmal den Prozess ersetzt und nicht den Thread 🙂 und zum Prozess gehören immer alle Threads.



  • Irgendwie hab ich bis jetzt nie im Detail darüber nachgedacht. Ich hab kurz zwei Artikel quer gelesen, jetzt ist mir das auch klar. In jedem Fall, vielen Dank für die Hilfe. 🙂


Log in to reply