Umleitung von stdin/stdout funktioniert nicht mit execlp
-
Folgendes Problem:
Ich möchte aus meinem eigenem Programm heraus ein anderes Programm aufrufen und die Standard Ein-/Ausgabe über mein Programm realisieren.
Nun bin ich schon soweit, dass ich die Umleitung mittels Pipes von stdin und stdout in einem geforkten Kinder-Prozess am laufen habe.
Eine einfache Schliefe im Kinder-Prozess die alle Eingaben von cin nach cout schreibt funktioniert.Wenn ich im Kinder-Prozess ein Programm mittels execlp ausführe funktioniert's leider nicht. Eingaben kommen nicht zum ausgeführten Programm und Ausgaben kommen erst nach Beendigung dieses Programmes aus der Pipe des Eltern-Prozesses.
Hier nun der source:
#include <iostream> using namespace std; int main() { int c2p[2], p2c[2]; pipe( c2p ); pipe( p2c ); int pid = fork(); if ( pid == 0 ){ int STDIN_dup2_result = dup2(p2c[0], STDIN_FILENO); if ( STDIN_dup2_result != STDIN_FILENO ){ cerr << "error dup2(p2c[0], STDIN_FILENO)" << endl; } int STDOUT_dup2_result = dup2(c2p[1], STDOUT_FILENO); if ( STDOUT_dup2_result != STDOUT_FILENO ){ cerr << "error dup2(c2p[1], STDOUT_FILENO)" << endl; } //Das funktioniert nicht execlp("cat", "cat", (char *) 0); //Das hier wuerde funktionieren: /* char ch; cin >> ch; */ } else { FILE *tochild, *fromchild; close(p2c[0]); tochild = fdopen( p2c[1], "w" ); if ( tochild == NULL ) { cout << "open tochild fail" << endl; } close(c2p[1]); fromchild = fdopen( c2p[0], "r" ); if ( fromchild == NULL ) { cout << "open fromchild fail" << endl; } //eine char zum Kinder-Prozess senden um es direkt wieder zurueckzubekommen fputc( '1', tochild ); cout << fgetc( fromchild ); } return 0; }
-
Ich habe mal versucht das Problem etwas einzukreisen. Schaut nach einem deadlock aus, auch wenn ich mir das nicht erklären kann.
Wenn der Kinder-Prozess auf die Eingabe cin lesen will wird es gestoppt weil die pipe noch leer ist. Nun wird der Eltern-Prozess ausgeführt und sendet an den Kinder-Prozess um anschließend durch die eigene Leseoperation auf die leere Pipe gestoppt zu werden. Nun müsste der Kinder-Prozess wieder ausgeführt werden um die Leseoperation von cin auszuführen was jedoch nicht der Fall ist. Das Programm hängt.
Ausgabe des Programmes:
Child: Warte auf eingabe von Parent.
Parent: Sende x an child.
Parent: Gesendet. Warte auf Antwort von child.
<Ab hier hängt das Programm>Wenn ich nur in eine Richtung sende funktioniert es einwandfrei. Auch wenn zuerst vom Kinder-Prozess zum Eltern-Prozess gesendet wird funktioniert alles.
Hier der Source wie es nicht funktioniert:
#include <iostream> using namespace std; int main() { int c2p[2], p2c[2]; pipe( c2p ); pipe( p2c ); int pid = fork(); if ( pid == 0 ){ int STDIN_dup2_result = dup2(p2c[0], STDIN_FILENO); if ( STDIN_dup2_result != STDIN_FILENO ){ cerr << "error STDIN_dup2_result" << endl; } int STDOUT_dup2_result = dup2(c2p[1], STDOUT_FILENO); if ( STDOUT_dup2_result != STDOUT_FILENO ){ cerr << "error STDOUT_dup2_result" << endl; } //ein char vom Eltern-Prozess empfangen um es direkt wieder zurueckzusenden char ch; cerr << "Child: Warte auf eingabe von Parent." << endl; cin >> ch; cerr << "Child: Eingabe von parent empfangen. Sende nun an Parent zurück." << endl; cout << ch; cerr << "Child: Ende" << endl; } else { FILE *tochild, *fromchild; close(p2c[0]); tochild = fdopen( p2c[1], "w" ); if ( tochild == NULL ) { cout << "open tochild faild" << endl; } close(c2p[1]); fromchild = fdopen( c2p[0], "r" ); if ( fromchild == NULL ) { cout << "open fromchild faild" << endl; } //ein char zum Kinder-Prozess senden um es direkt wieder zurueckzubekommen cerr << "Parent: Sende x an child." << endl; fputc( 'x', tochild ); cerr << "Parent: Gesendet. Warte auf Antwort von child." << endl; cout << fgetc( fromchild ); cerr << "Parent: Antwort empfangen. Ende." << endl; } return 0; }
-
Dieses alte IPC-Problem nennt sich Buffering (ein Satz aus diversen Perl-Dokumentationen, der sich mir ins Gedächtnis eingebrannt hat lautet "Unix I/O-Buffering is really going to ruin your day"). Der schreibende Prozess schickt die Daten noch nicht ab, weil noch genug Platz im Ausgabepuffer ist, während der lesende Prozess verzweifelt auf eben diese Daten wartet.
Solange Du beide Programme selbst in der Hand hast, kannst Du das Schreiben des Puffers mit fflush(filepointer) erzwingen.
-
Danke! Es funktioniert nun einwandfrei.
Schade dass dieser Sachverhalt in keinem Pipe-Tutorial stand welche ich gelesen habe.
Noch eine Frage zum Abschluss. Gibt es eine C++ Variante für Pipes. Ich habe einige Wraper gefunden aber war mir nicht sicher welche die beste sein würde. Ich werde mir bei Gelegenheit die boost-lib anschauen.