In Programm 1 Hello world
wird nur einmal gedruckt, aber wenn ich es entferne \n
und ausführe (Programm 2), wird die Ausgabe 8 Mal gedruckt. Kann mir bitte jemand erklären, welche Bedeutung das \n
hier hat und wie sich das auswirkt fork()
?
Programm 1
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("hello world...\n");
fork();
fork();
fork();
}
Ausgang 1:
hello world...
Programm 2
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("hello world...");
fork();
fork();
fork();
}
Ausgang 2:
hello world... hello world...hello world...hello world...hello world...hello world...hello world...hello world...
./prog1 > prog1.out
) oder eine Pipe (./prog1 | cat
) auszuführen . Bereite dich darauf vor, deinen Verstand durchzubrennen. :-) fork()
sind auch etwas unix-spezifisch. Daher scheint dies für unix.SE ein ziemlich aktuelles Thema zu sein.Antworten:
Bei der Ausgabe auf Standardausgabe mit der
printf()
Funktion der C-Bibliothek wird die Ausgabe normalerweise gepuffert. Der Puffer wird erst geleert, wenn Sie eine neue Zeile ausgeben,fflush(stdout)
das Programm aufrufen oder beenden (jedoch nicht durch Aufrufen_exit()
). Der Standardausgabestream wird auf diese Weise standardmäßig zeilenweise gepuffert, wenn er mit einem TTY verbunden ist.Wenn Sie den Prozess in "Programm 2" verzweigen, erben die untergeordneten Prozesse alle Teile des übergeordneten Prozesses, einschließlich des nicht leeren Ausgabepuffers. Dadurch wird der nicht gelöschte Puffer effektiv auf jeden untergeordneten Prozess kopiert.
Wenn der Prozess beendet ist, werden die Puffer geleert. Sie starten insgesamt acht Prozesse (einschließlich des ursprünglichen Prozesses), und der nicht geleerte Puffer wird nach Beendigung jedes einzelnen Prozesses geleert.
Es ist acht, weil Sie bei jedem
fork()
die doppelte Anzahl von Prozessen erhalten, die Sie vor dem hattenfork()
(da sie bedingungslos sind), und Sie haben drei von diesen (2 3 = 8).quelle
main
mit_exit(0)
nur einen Ausgang Systemaufruf ohne Spülung Puffer zu machen, und dann wird es Null mal ohne eine neue Zeile gedruckt werden. ( Syscall-Implementierung von exit () und How come _exit (0) (wird von syscall beendet) verhindert, dass ich Standardinhalte erhalte? ). Oder Siecat
leiten Program1 in eine Datei um oder leiten es in eine Datei um und sehen, dass es 8 Mal gedruckt wird. (stdout ist standardmäßig voll gepuffert, wenn es kein TTY ist). Oder fügen Siefflush(stdout)
dem No-Newline-Fall vor dem 2.fork()
...Es beeinflusst die Gabel in keiner Weise.
Im ersten Fall haben Sie 8 Prozesse, bei denen Sie nichts zu schreiben haben, da der Ausgabepuffer (aufgrund der
\n
) bereits geleert wurde .Im zweiten Fall haben Sie noch 8 Prozesse, jeder mit einem Puffer, der "Hallo Welt ..." enthält, und der Puffer wird am Ende des Prozesses geschrieben.
quelle
@ Kusalananda erklärte, warum die Ausgabe wiederholt wird . Wenn Sie neugierig sind, warum die Ausgabe 8 Mal und nicht nur 4 Mal wiederholt wird (das Basisprogramm + 3 Gabeln):
quelle
Der wichtige Hintergrund hierbei ist, dass
stdout
die Leitung standardmäßig gepuffert sein muss .Dies bewirkt, dass a
\n
den Ausgang spült.Da das zweite Beispiel keine neue Zeile enthält, wird die Ausgabe nicht geleert und
fork()
kopiert als Kopie des gesamten Prozesses auch den Zustand desstdout
Puffers.Mit diesen
fork()
Aufrufen in Ihrem Beispiel werden insgesamt 8 Prozesse erstellt - alle mit einer Kopie des Status desstdout
Puffers.Per Definition rufen alle diese Prozesse
exit()
bei der Rückkehr vonmain()
und beiexit()
Aufrufenfflush()
gefolgt vonfclose()
allen aktiven stdio- Streams auf. Dies schließt einstdout
und als Ergebnis wird derselbe Inhalt achtmal angezeigt.Es ist empfehlenswert, vor dem Aufrufen
fflush()
alle Streams mit ausstehender Ausgabe aufzurufenfork()
oder den untergeordneten Zweig explizit aufrufen zu lassen, der_exit()
den Prozess nur beendet, ohne die stdio-Streams zu leeren.Beachten Sie, dass beim Aufrufen
exec()
die Stdio-Puffer nicht geleert werden. Daher ist es in Ordnung, sich nicht um die Stdio-Puffer zu kümmern, wenn Sie (nach dem Aufrufenfork()
) anrufenexec()
und (falls dies fehlschlägt) anrufen_exit()
.Übrigens: Um zu verstehen, dass eine falsche Pufferung dazu führen kann, ist hier ein früherer Fehler in Linux, der kürzlich behoben wurde:
Der Standard muss
stderr
standardmäßig nichtstderr
gepuffert sein , aber Linux hat dies ignoriert und die Zeilen gepuffert und (noch schlimmer) vollständig gepuffert, falls stderr über eine Pipe umgeleitet wurde. Programme, die für UNIX geschrieben wurden, gaben also unter Linux Dinge ohne Zeilenumbruch zu spät aus.Siehe Kommentar unten, es scheint jetzt behoben zu sein.
Folgendes tue ich, um dieses Linux-Problem zu umgehen:
Dieser Code schadet auf anderen Plattformen nicht, da das Aufrufen
fflush()
eines gerade gelöschten Streams ein Noop ist.quelle
setbuf()
Debian ( die auf man7.org sieht ähnlich aus ) heißt es: "Der Standard-Fehlerstrom stderr ist standardmäßig immer ungepuffert." und ein einfacher Test scheint so zu verfahren, unabhängig davon, ob die Ausgabe an eine Datei, eine Pipe oder ein Terminal gesendet wird. Haben Sie eine Referenz für welche Version der C-Bibliothek, die sonst tun würde?