Ich stelle fest, dass dies <system.h>
ein nicht standardmäßiger Header ist. Ich habe es durch ersetzt <unistd.h>
und den Code sauber kompiliert.
Wenn die Ausgabe Ihres Programms an ein Terminal (Bildschirm) geht, wird es zeilengepuffert. Wenn die Ausgabe Ihres Programms an eine Pipe geht, ist sie vollständig gepuffert. Sie können den Puffermodus mit der Standard-C-Funktion setvbuf()
und den Modi _IOFBF
(vollständige Pufferung), _IOLBF
(Zeilenpufferung) und _IONBF
(keine Pufferung) steuern.
Sie können dies in Ihrem überarbeiteten Programm demonstrieren, indem Sie beispielsweise die Ausgabe Ihres Programms an Folgendes weiterleiten cat
. Selbst mit den Zeilenumbrüchen am Ende der printf()
Zeichenfolgen würden Sie die doppelte Information sehen. Wenn Sie es direkt an das Terminal senden, sehen Sie nur die eine Menge Informationen.
Die Moral der Geschichte ist es, vorsichtig zu sein, fflush(0);
um alle E / A-Puffer vor dem Gabeln zu leeren.
Zeilenweise Analyse nach Bedarf (Klammern usw. entfernt - und führende Leerzeichen vom Markup-Editor entfernt):
printf( "Hello, my pid is %d", getpid() );
pid = fork();
if( pid == 0 )
printf( "\nI was forked! :D" );
sleep( 3 );
else
waitpid( pid, NULL, 0 );
printf( "\n%d was forked!", pid );
Die Analyse:
- Kopiert "Hallo, meine PID ist 1234" in den Puffer für die Standardausgabe. Da am Ende keine neue Zeile vorhanden ist und die Ausgabe im zeilengepufferten Modus (oder im voll gepufferten Modus) ausgeführt wird, wird auf dem Terminal nichts angezeigt.
- Gibt uns zwei separate Prozesse mit genau demselben Material im Standardpuffer.
- Das Kind hat
pid == 0
und führt die Zeilen 4 und 5 aus; Das übergeordnete Element hat einen Wert ungleich Null für pid
(einer der wenigen Unterschiede zwischen den beiden Prozessen - Rückgabewerte von getpid()
und getppid()
sind zwei weitere).
- Fügt dem Ausgabepuffer des Kindes eine neue Zeile und "Ich wurde gegabelt !: D" hinzu. Die erste Ausgabezeile wird auf dem Terminal angezeigt. Der Rest wird im Puffer gehalten, da die Ausgabe zeilengepuffert ist.
- Alles hält für 3 Sekunden an. Danach verlässt das Kind normal durch die Rückkehr am Ende der Hauptleitung. Zu diesem Zeitpunkt werden die Restdaten im Standardpuffer gelöscht. Dadurch bleibt die Ausgabeposition am Ende einer Zeile, da keine neue Zeile vorhanden ist.
- Der Elternteil kommt hierher.
- Der Elternteil wartet darauf, dass das Kind mit dem Sterben fertig ist.
- Der Elternteil fügt eine neue Zeile hinzu und "1345 wurde gegabelt!" zum Ausgabepuffer. Die neue Zeile löscht die Nachricht "Hallo" nach der vom Kind generierten unvollständigen Zeile in die Ausgabe.
Das übergeordnete Element wird jetzt normal durch die Rückgabe am Ende von main beendet, und die verbleibenden Daten werden gelöscht. Da am Ende noch keine neue Zeile steht, befindet sich die Cursorposition hinter dem Ausrufezeichen und die Shell-Eingabeaufforderung wird in derselben Zeile angezeigt.
Was ich sehe ist:
Osiris-2 JL: ./xx
Hello, my pid is 37290
I was forked! :DHello, my pid is 37290
37291 was forked!Osiris-2 JL:
Osiris-2 JL:
Die PID-Nummern sind unterschiedlich - aber das allgemeine Erscheinungsbild ist klar. Das Hinzufügen von Zeilenumbrüchen am Ende der printf()
Anweisungen (was sehr schnell zur Standardpraxis wird) verändert die Ausgabe erheblich :
#include <stdio.h>
#include <unistd.h>
int main()
{
int pid;
printf( "Hello, my pid is %d\n", getpid() );
pid = fork();
if( pid == 0 )
printf( "I was forked! :D %d\n", getpid() );
else
{
waitpid( pid, NULL, 0 );
printf( "%d was forked!\n", pid );
}
return 0;
}
Ich bekomme jetzt:
Osiris-2 JL: ./xx
Hello, my pid is 37589
I was forked! :D 37590
37590 was forked!
Osiris-2 JL: ./xx | cat
Hello, my pid is 37594
I was forked! :D 37596
Hello, my pid is 37594
37596 was forked!
Osiris-2 JL:
Beachten Sie, dass die Ausgabe, wenn sie an das Terminal gesendet wird, zeilengepuffert ist, sodass die Zeile "Hallo" vor dem angezeigt wird fork()
und nur eine Kopie vorhanden war. Wenn die Ausgabe an weitergeleitet wird, cat
ist sie vollständig gepuffert, sodass vor dem fork()
und beide Prozesse die Zeile "Hallo" im zu löschenden Puffer angezeigt wird.
Der Grund dafür ist, dass
\n
der Wert ohne die am Ende der Formatzeichenfolge nicht sofort auf dem Bildschirm gedruckt wird. Stattdessen wird es innerhalb des Prozesses gepuffert. Dies bedeutet, dass es erst nach dem Gabelvorgang gedruckt wird, sodass Sie es zweimal drucken lassen.Durch Hinzufügen des
\n
obwohl wird der Puffer geleert und auf dem Bildschirm ausgegeben. Dies geschieht vor der Gabel und wird daher nur einmal gedruckt.Sie können dies mithilfe der
fflush
Methode erzwingen . Zum Beispielprintf( "Hello, my pid is %d", getpid() ); fflush(stdout);
quelle
fflush(stdout);
Scheint hier imo die richtigere Antwort zu sein.fork()
erstellt effektiv eine Kopie des Prozesses. Wenn vor dem Aufruffork()
Daten gepuffert waren, haben sowohl das übergeordnete als auch das untergeordnete Element dieselben gepufferten Daten. Wenn jeder von ihnen das nächste Mal etwas unternimmt, um seinen Puffer zu leeren (z. B. das Drucken einer neuen Zeile bei Terminalausgabe), wird diese gepufferte Ausgabe zusätzlich zu jeder neuen Ausgabe angezeigt, die durch diesen Prozess erzeugt wird. Wenn Sie also stdio sowohl für Eltern als auch für Kinder verwendenfflush
möchten, sollten Sie dies vor dem Verzweigen tun, um sicherzustellen, dass keine gepufferten Daten vorhanden sind.Oft wird das Kind nur zum Aufrufen einer
exec*
Funktion verwendet. Da dies das gesamte untergeordnete Prozessabbild (einschließlich aller Puffer) ersetzt, ist dies technisch nicht erforderlich,fflush
wenn dies wirklich alles ist, was Sie im untergeordneten Prozess tun werden. Wenn jedoch möglicherweise gepufferte Daten vorhanden sind, sollten Sie vorsichtig sein, wie ein Exec-Fehler behandelt wird. Vermeiden Sie es insbesondere, den Fehler mit einer beliebigen stdio-Funktion auf stdout oder stderr zu drucken (write
ist in Ordnung), und rufen Sie dann_exit
(oder_Exit
) auf, anstatt aufzurufenexit
oder nur zurückzukehren (wodurch alle gepufferten Ausgaben gelöscht werden). Oder vermeiden Sie das Problem vollständig, indem Sie vor dem Gabeln spülen.quelle