warum kann ich die Ausgabe von beiden execl-Aufrufen nicht weiterleiten?
Frage
Mögliche Duplikate:
- Wie rufe ich execl () in C mit den richtigen Argumenten auf?
- Ausgabe von exec abrufen
- Linux-Pipes als Eingabe und Ausgabe
- Verwendung von dup2 für Rohrleitungen
- Rohrleitungen für Eingabe / Ausgabe
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!
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);