Frage

Mögliche Duplikate:

Ich habe in den letzten 3 Tagen versucht, Piping unter Linux mit dup / dup2 und Fork zu lernen.Ich glaube, ich habe den Dreh raus, aber wenn ich zwei verschiedene Programme aus dem untergeordneten Prozess aufrufe, scheint es, dass ich nur die Ausgabe des ersten aufgerufenen Programms erfasse. Ich verstehe nicht, warum das so ist und / oder was ich falsch mache.Das ist meine Hauptfrage.

Bearbeiten:Ich denke, eine mögliche Lösung besteht darin, ein anderes Kind zu verzweigen und Pipes mit dup2 einzurichten, aber ich frage mich eher, warum der folgende Code nicht funktioniert.Was ich meine ist, ich würde erwarten, stderr vom ersten execl-Aufruf und stdout vom zweiten zu erfassen.Das scheint nicht zu passieren.

Meine zweite Frage ist, ob ich die Rohre richtig öffne und schließe.Wenn nicht, würde ich gerne wissen, was ich hinzufügen / entfernen / ändern muss.

Hier ist mein Code:

#include <stdlib.h>
#include <iostream>
#include <time.h>
#include <sys/wait.h>

#define READ_END 0
#define WRITE_END 1

void parentProc(int* stdoutpipe, int* stderrpipe);
void childProc(int* stdoutpipe, int* stderrpipe);

int main(){
    pid_t pid;
    int status;

    int stdoutpipe[2]; // pipe
    int stderrpipe[2]; // pipe

    // create a pipe
    if (pipe(stdoutpipe) || pipe(stderrpipe)){
        std::cerr << "Pipe failed." << std::endl;
        return EXIT_FAILURE;
    }

    // fork a child
    pid = fork();
    if (pid < 0) {
        std::cerr << "Fork failed." << std::endl;
        return EXIT_FAILURE;
    } 
    // child process
    else if (pid == 0){
        childProc(stdoutpipe, stderrpipe);  

    }
    // parent process
    else {
        std::cout<< "waitpid: " << waitpid(pid, &status, 0)
             <<'\n'<<std::endl;
        parentProc(stdoutpipe, stderrpipe);
    }

    return 0;
}



void childProc(int* stdoutpipe, int* stderrpipe){
    dup2(stdoutpipe[WRITE_END], STDOUT_FILENO);
    close(stdoutpipe[READ_END]);

    dup2(stderrpipe[WRITE_END], STDERR_FILENO);
    close(stderrpipe[READ_END]);

    execl("/bin/bash", "/bin/bash", "foo", NULL);
    execl("/bin/ls", "ls", "-1", (char *)0);

    //  execl("/home/me/outerr", "outerr", "-1", (char *)0);

    //char * msg = "Hello from stdout";
    //std::cout << msg;

    //msg = "Hello from stderr!";
    //std::cerr << msg << std::endl;

    // close write end now?
}

void parentProc(int* stdoutpipe, int* stderrpipe){
    close(stdoutpipe[WRITE_END]);
    close(stderrpipe[WRITE_END]);

    char buffer[256];
    char buffer2[256];
    read(stdoutpipe[READ_END], buffer, sizeof(buffer));
    std::cout << "stdout: " << buffer << std::endl;

    read(stderrpipe[READ_END], buffer2, sizeof(buffer));
    std::cout << "stderr: " << buffer2 << std::endl;

    // close read end now?
}

Wenn ich das ausführe, erhalte ich die folgende Ausgabe:

yfp> g++ selectTest3.cpp; ./a.out 
waitpid: 21423

stdout: hB�(6
stderr: foo: line 1: -bash:: command not found

Der Quellcode für die Binärdatei "outerr" (oben auskommentiert) lautet einfach:

#include <iostream>

int main(){
    std::cout << "Hello from stdout" << std::endl;
    std::cerr << "Hello from stderr!" << std::endl;
    return 0;
}

Wenn ich "outerr" anstelle von ls oder "foo" aufrufe, erhalte ich die folgende Ausgabe, die ich erwarten würde:

yfp> g++ selectTest3.cpp; ./a.out 
waitpid: 21439

stdout: Hello from stdout

stderr: Hello from stderr!
War es hilfreich?

Lösung

Auf execl

Sobald Sie erfolgreich anrufen execl oder eine andere Funktion aus dem exec familie, der ursprüngliche Prozess wird vollständig durch den neuen Prozess überschrieben.Dies bedeutet, dass der neue Prozess niemals zum alten "zurückkehrt".Wenn Sie zwei haben execl aufrufe in einer Reihe, die zweite kann nur ausgeführt werden, wenn die erste fehlschlägt.

Um zwei verschiedene Befehle hintereinander auszuführen, müssen Sie fork ein Kind, das den ersten Befehl ausführt, wait, fork ein zweites Kind, um den zweiten Befehl auszuführen, dann (optional) wait auch für das zweite Kind.

Auf read

Der read der Systemaufruf hängt keine abschließende Null an, daher müssen Sie sich im Allgemeinen den Rückgabewert ansehen, der Ihnen die Anzahl der tatsächlich gelesenen Bytes angibt.Setzen Sie dann das folgende Zeichen auf null, um eine C-Zeichenfolge zu erhalten, oder verwenden Sie den Bereichskonstruktor für std::string.

Auf Rohren

Im Moment verwenden Sie waitpid warten, bis der untergeordnete Prozess bereits abgeschlossen ist, dann aus den Rohren lesen.Das Problem dabei ist, dass, wenn der untergeordnete Prozess viel Ausgabe erzeugt, dies der Fall ist Block weil die Pipe voll wird und der übergeordnete Prozess nicht daraus liest.Das Ergebnis ist ein Deadlock, da das Kind darauf wartet, dass das Elternteil liest und das Elternteil darauf wartet, dass das Kind beendet wird.

Was Sie tun sollten, ist zu verwenden select warten, bis die Eingabe entweder auf dem Konto des Kindes eintrifft stdout oder des Kindes stderr.Wenn die Eingabe eintrifft, lesen Sie sie;dadurch kann das Kind weitermachen.Wenn der untergeordnete Prozess stirbt, werden Sie es wissen, weil Sie bei beiden das Dateiende erhalten. Dann sie können sicher anrufen wait oder waitpid.

Andere Tipps

Der exec funktionsfamilie Ersetzen Sie das aktuelle Prozessabbild durch ein neues Prozessabbild.Wenn Sie ausführen,

execl("/bin/bash", "/bin/bash", "foo", NULL);

der Code aus dem aktuellen Prozess wird nicht mehr ausgeführt.Deshalb sehen Sie nie das Ergebnis der Ausführung

execl("/bin/ls", "ls", "-1", (char *)0);
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top