Le programme 1 Hello world
est imprimé une seule fois, mais lorsque je le supprime \n
et le lance (programme 2), la sortie est imprimée 8 fois. Quelqu'un peut-il m'expliquer s'il vous plaît la signification de \n
ici et comment cela affecte le fork()
?
Programme 1
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("hello world...\n");
fork();
fork();
fork();
}
Sortie 1:
hello world...
Programme 2
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("hello world...");
fork();
fork();
fork();
}
Sortie 2:
hello world... hello world...hello world...hello world...hello world...hello world...hello world...hello world...
./prog1 > prog1.out
) ou un tuyau (./prog1 | cat
). Préparez-vous à avoir l'esprit brisé. :-) fork()
sont quelque peu spécifiques à Unix également.Réponses:
Lors de la sortie sur une sortie standard à l'aide de la
printf()
fonction de la bibliothèque C , la sortie est généralement mise en mémoire tampon. La mémoire tampon n'est pas vidée jusqu'à ce que vous sortiez une nouvelle ligne, appelezfflush(stdout)
ou quittez le programme (pas via l'appel_exit()
). Le flux de sortie standard est par défaut mis en mémoire tampon de ligne de cette manière lorsqu'il est connecté à un TTY.Lorsque vous branchez le processus dans "Programme 2", les processus enfants héritent de toutes les parties du processus parent, y compris le tampon de sortie non vidé. Cela copie efficacement le tampon non vidé dans chaque processus enfant.
Lorsque le processus se termine, les tampons sont vidés. Vous démarrez un total de huit processus (y compris le processus d'origine) et le tampon non vidé sera vidé à la fin de chaque processus.
C'est huit parce qu'à chaque fois,
fork()
vous obtenez deux fois le nombre de processus que vous aviez avantfork()
(puisqu'ils sont inconditionnels), et vous en avez trois (2 3 = 8).la source
main
avec_exit(0)
juste faire un appel système de sortie sans tampons de rinçage, puis il sera imprimé zéro fois sans retour à la ligne. ( L'implémentation syscall de exit () et comment _exit (0) (sortie par syscall) m'empêche de recevoir du contenu stdout? ). Ou vous pouvez diriger Program1 danscat
ou rediriger vers un fichier et le voir être imprimé 8 fois. (stdout est mis en mémoire tampon par défaut lorsqu'il ne s'agit pas d'un téléscripteur). Ou ajouter unfflush(stdout)
à l'affaire no-newline avant le 2fork()
...Cela n'affecte en rien la fourche.
Dans le premier cas, vous vous retrouvez avec 8 processus sans rien à écrire, car le tampon de sortie a déjà été vidé (en raison de la
\n
).Dans le second cas, vous avez encore 8 processus, chacun avec un tampon contenant "Hello world ..." et le tampon est écrit à la fin du processus.
la source
@Kusalananda a expliqué pourquoi la sortie est répétée . Si vous êtes curieux de savoir pourquoi la sortie est répétée 8 fois et pas seulement 4 fois (le programme de base + 3 fourchettes):
la source
L'arrière-plan important ici est qu'il
stdout
est nécessaire que la norme mette en mémoire tampon la configuration par défaut.Cela provoque un
\n
vidage de la sortie.Comme le deuxième exemple ne contient pas de nouvelle ligne, la sortie n'est pas vidée et, en tant que
fork()
copie du processus entier, elle copie également l'état dustdout
tampon.Maintenant, ces
fork()
appels dans votre exemple créent 8 processus au total - tous avec une copie de l'état dustdout
tampon.Par définition, tous ces processus appellent
exit()
lors du retour depuismain()
et desexit()
appelsfflush()
suivis parfclose()
sur tous les flux stdio actifs . Cela inclutstdout
et par conséquent, vous voyez le même contenu huit fois.Il est recommandé d’appeler
fflush()
tous les flux avec une sortie en attente avant d’appelerfork()
ou de laisser l’appel enfant appelé explicitement_exit()
qui ne fait que quitter le processus sans vider les flux stdio.Notez que l'appel
exec()
ne vide pas les tampons stdio. Vous pouvez donc ne pas vous soucier des tampons stdio si vous appelez (après l'appelfork()
)exec()
et si vous échouez_exit()
.BTW: Pour comprendre ce que la mise en mémoire tampon incorrecte peut causer, voici un ancien bogue sous Linux qui a été récemment corrigé:
La norme nécessite de ne
stderr
pas mettre lastderr
mémoire tampon par défaut, mais Linux l'a ignoré et a fait en sorte que la ligne soit tamponnée et (pire encore) complètement mise en mémoire tampon au cas où stderr serait redirigé via un tube. Les programmes écrits pour UNIX produisaient donc des données sans nouvelle ligne trop tard sous Linux.Voir le commentaire ci-dessous, il semble être corrigé maintenant.
Voici ce que je fais pour contourner ce problème Linux:
Ce code ne nuit pas aux autres plates-formes, car appeler
fflush()
sur un flux qui vient d'être vidé est un noop.la source
setbuf()
, sur Debian ( celle de man7.org est similaire ), indique que "Le flux d'erreur standard stderr est toujours désactivé par défaut." et un simple test semble agir de cette manière, que la sortie soit dirigée vers un fichier, un canal ou un terminal. Avez-vous des références pour quelle version de la bibliothèque C ferait autrement?