Lesen vom einem Child-Prozess funktioniert nur einmal



  • Heho!

    Ich bin nicht unbedingt der erfahrenste Unix-Programmieren und bin auf einen Bug gestoßen, den ich nicht ausfindig machen kann.
    Mein Ziel ist folgendes: Einen Kind Prozess zu erstellen (fork) Daten über Pipe A zu diesem zu senden (woraufhin der Prozess Berechnungen durchführt) und über Pipe B von diesem zu empfangen.
    Der unten aufgeführte Code arbeitet bei der ersten Ausführung problemlos. Bei der zweiten Ausführung werden jedoch keine Daten empfangen, die der Child-Prozess an den Parent-Prozess sendet. Daher endet die Schleife

    while (std::cin >> lineInput || result == "")
    

    in einer Endlosschleife. Wenn ich

    read
    

    anstelle von

    cin
    

    verwende, werden Daten empfangen scheinbar aber nur ein Bruchteil (auch schon bei der ersten Ausführung). Über Hinweise und Vorschläge jeglicher Art bin ich sehr dankbar!

    std::string result;
    
    pid_t pid;
    int pipeA[2];
    int pipeB[2];
    
    if(pipe(pipeA) < 0) perror("pipe a failed"); // To Child
    if(pipe(pipeB) < 0) perror("pipe b failed"); // From Child
    
    pid = fork();
    if(pid == -1){
    	perror("fork failed");
    	exit(1);
    }
    else if(pid == 0){
    	if(close(pipeA[1])<0) perror("c close a1 failed"); // No Writing in A
    	if(close(pipeB[0])<0) perror("c close b0 failed"); // No Reading B
    	if(dup2(pipeA[0], STDIN_FILENO)==-1) perror("c dub a failed");
    	if(dup2(pipeB[1], STDOUT_FILENO)==-1) perror("c dub b failed");
    
    	std::cerr << "child process starting ..." << std::endl;
    
    	std::stringstream sstr;
    	sstr << frequency;
    	execl("../helloworld/app", "app", (char*)0);
    
    	if(close(pipeA[0])<0) perror("c close a0 failed");
    	if(close(pipeB[1])<0) perror("c close b1 failed");
    
    	std::cerr << "child stopping ..." << std::endl;
    	exit(1);
    }
    else{
    	if(close(pipeA[0])<0) perror("close a0 failed"); // No Reading from A
    	if(close(pipeB[1])<0) perror("close b1 failed"); // No Writing to B
    	//if(dup2(pipeA[1], STDOUT_FILENO)==-1) perror("dub a failed"); // Write A
    	if(dup2(pipeB[0], STDIN_FILENO)==-1) perror("dub b failed"); // Read B
    
    	// write pipe
    	FILE* stream;
    	stream = fdopen (pipeA[1], "w");
    	if(stream == 0) perror("stream failed");
    	fwrite(&midi[0], midi.size(), sizeof(BYTE), stream);
    	fflush(stream);
    	fclose(stream);
    
    	std::cout << "Start reading from position: " 
                      << lseek(pipeB[0], 0, SEEK_CUR) << std::endl;
    
    	// read pipe
    	char buf[1024];
    	if(toFile == "") {
    
    		// works second time, but not complete?
    		//while (read(pipeB[0], &buf, 1024) > 0 || result == "")
    		//    result += buf;
    
    		// Does not work second time, no chars received
    		std::string lineInput;
    		while (std::cin >> lineInput || result == "")
    			result.append(lineInput);
    	}
    	// close pipes
    	//if(close (pipeA[1])<0) perror("close a1 failed");
    	if(close (pipeB[0])<0) perror("close b0 failed");
    	if(kill(pid,SIGKILL)<0) perror("sigkill failed");
    }
    


  • Ist dir klar, dass der Code hinter execl nur ausgeführt wird, wenn execl fehlschlägt? Im Erfolgsfall kommt diese Funkion nicht zurück.



  • Oh, das hatte ich nicht bedacht gehabt. Aber auch, wenn das Lese- / Schreibende im Child-Prozess von Pipe A / B nicht geschlossen wird, sollte das keine Auswirkungen haben, oder?
    Wäre das überhaupt möglich, einen file descriptor nach einem execute zu schließen?



  • Mit

    read
    

    funktioniert das Programm jetzt. Das falsche Daten ausgegeben wurden lag an der Zeile

    result += buf;
    

    Man muss natürlich schauen wie viele Daten gelesen wurden und entsprechend an das Ergebnis anhängen. Ansonsten wird wohl nur bis zum ersten

    '\0'
    

    gelesen. Trotzdem bleibt es mir noch ein Rätsel weshalb die gleiche Methode beim zweiten Aufruf von

    std::cin
    

    hängen blieb.



  • JavaJim schrieb:

    Oh, das hatte ich nicht bedacht gehabt. Aber auch, wenn das Lese- / Schreibende im Child-Prozess von Pipe A / B nicht geschlossen wird, sollte das keine Auswirkungen haben, oder?

    Doch und zwar gewaltige. Wenn der Reader sein Write-Ende der Pipe nicht schließt, würde er kein EOF signalisiert bekommen und im read(2)-Call blockieren. Umgekehrt würde auch der Writer blockieren (zumindest sobald er den Pipe-Buffer gefüllt hat), solange noch ein Read-Ende der Pipe offen ist. Auch wenn der eigentliche Reader seinen FD längst geschlossen hat und niemals davon lesen wird. Normalerweise wird er über das Ableben bzw. Schließen der Pipe seitens Reader durch SIGPIPE/EPIPE informiert und kann entsprechend reagieren.

    Es geht also mitnichten nur um die Ressourcen, die der Kernel für die Pipe bereit halten muss, solange FDs dafür existieren, sondern auch um die Korrektheit deines Programms.

    Wäre das überhaupt möglich, einen file descriptor nach einem execute zu schließen?

    Ja, ist möglich. In der Regel wird das aber einfach vor dem exec gemacht.



  • Eigentlich suchst du die Funktion popen.



  • Das Problem an popen ist, dass man nur eine Pipe zur Verfügung hat. Da ich aber zu dem Prozess schreiben und von ihm lesen möchte, benötige ich zwei Pipes. Deshalb hatte ich die Pipes per Hand erstellt.


Anmelden zum Antworten