CONNEXION
  • RetourJeux
    • Sorties
    • Hit Parade
    • Les + populaires
    • Les + attendus
    • Soluces
    • Tous les Jeux
    • Gaming
  • RetourActu Gaming
    • News
    • Astuces
    • Tests
    • Previews
    • Toute l'actu gaming
  • RetourBons plans
    • Bons plans
    • Bons plans Smartphone
    • Bons plans Hardware
    • Bons plans Image et Son
    • Bons plans Amazon
    • Bons plans Cdiscount
    • Bons plans Decathlon
    • Bons plans Fnac
    • Tous les Bons plans
  • RetourJVTech
    • Actus High-Tech
    • Intelligence Artificielle
    • Smartphones
    • Mobilité urbaine
    • Hardware
    • Image et son
    • Tutoriels
    • Tests produits High-Tech
    • Guides d'achat High-Tech
    • JVTech
  • RetourCulture
    • Actus Culture
    • Culture
  • RetourVidéos
    • A la une
    • Gaming Live
    • Vidéos Tests
    • Vidéos Previews
    • Gameplay
    • Trailers
    • Chroniques
    • Replay Web TV
    • Toutes les vidéos
  • RetourForums
    • Hardware PC
    • PS5
    • Switch 2
    • Xbox Series
    • Switch
    • Pokemon pocket
    • FC 25 Ultimate Team
    • League of Legends
    • Tous les Forums
  • PC
  • PS5
  • Xbox Series
  • Switch 2
  • PS4
  • One
  • Switch
  • iOS
  • Android
  • MMO
  • RPG
  • FPS
En ce moment Genshin Impact Valhalla Breath of the wild Animal Crossing GTA 5 Red dead 2
Liste des sujets

[C++] iostream buffering flush et endl

godrik
godrik
Niveau 30
09 juillet 2010 à 23:23:48

bonjour a vous,

J'ai une question sur le buffering des flux en C++. J'ai cru comprendre que tous les flux bufferise sont flusher lors que l'on insere std::endl (source : http://www.cplusplus.com/reference/iostream/manipulators/endl/ )

cependant je n'ai pas l'impression que c'est le cas sur les fichiers (objet std::ofstream). Considerer l'exemple en fin de message. Je cree un pipe nomme dans le systeme de ficheir et affiche son contenu avec "tail -f". Un code C++ ouvre ce fichier ecrit "schedule" et utilise std::endl. Il attend ensuite indefiniment. Rien n'apparait sur le terminal jusqu'a ce que je presse Ctrl-C qui tue le process et flush les buffers. Ajouter un out->flush() vide le buffer instantanement.

Avez vous une explication pour ce comportement qui ne semble pas etre celui attendu ?

Merci a vous,

erik@powell:/tmp$ mkfifo foo.in
erik@powell:/tmp$ tail -f foo.in &
[1] 2086
erik@powell:/tmp$ cat foo.cpp

  1. include <iostream>
  2. include <fstream>

int main ()
{
std::ostream* out = NULL;
out = new std::ofstream("foo.in");
*out<<"schedule"<<std::endl;
while(1);
return 0;
}
erik@powell:/tmp$ make foo
g++ foo.cpp -o foo
erik@powell:/tmp$ ./foo
^Cschedule
erik@powell:/tmp$

dnob700
dnob700
Niveau 10
10 juillet 2010 à 12:47:17

Oui, je dirais qu'il y a le buffering du système qui est à l'œuvre. Je ne sais pas si il y a une fonction en C++ pour connaitre l'état de remplissage du buffer d'un flux de sortie, mais je pense qu'il est vide après ton écriture de std::endl.

Par contre le système n'écrit pas les fichiers sur le disque en permanence, ça serait du gachis.

Peut-être que tu peux essayer de réécrire ton programme en C avec des flux unix sans buffer (open et write, plutôt que fopen et fprintf) pour voir si tu as le même comportement et dans ce cas essayer d'ouvrir ton fichier avec les options "O_SYNC | O_DIRECT" qui forcent l'écriture direct du fichier (peut-être que O_DIRECT suffit, tu n'as pas besoin que le fichier soit écrit sur le disque, mais seulement qui soit écrit dans le buffer du système).

godrik
godrik
Niveau 30
10 juillet 2010 à 23:40:10

mmm, en fait si flush le buffer dans mon code C++ (Par exemple avec le code en fin de message), alors le message apparait. Donc je ne pense pas que ce soit le buffer du systeme que je vois. Sauf si un appel a std::ofstream::flush() finit par faire un appel systeme a sync(2). J'essayerit ce dont tu parles pour etre sur.

  1. include <iostream>
  2. include <fstream>

int main ()
{
std::ostream* out = NULL;
out = new std::ofstream("foo.in");

  • out<<"schedule"<<std::endl;

out->flush();
while(1);
return 0;
}

godrik
godrik
Niveau 30
12 juillet 2010 à 17:54:04

_skip, endl est cense faire un appel flush. Mais je constate que ca ne le fait pas. Est ce que ca touche un autre buffer comme le suggere dnob ? peut etre mais je n'y crois pas, la seule difference entre les deux codes est un flush.

J'utilise une debian-lenny pour developper. Est ce que quelqu'un peut me dire si le comportement est le meme avec une autre version ? Je commence a me demander si c'est un bug dans iostream ou si je fais qqch de travers...

Pour information :

erik@powell:/tmp$ aptitude show libstdc++6-4.3-dev | grep Version
Version: 4.3.2-1.1

godrik
godrik
Niveau 30
12 juillet 2010 à 18:14:01

roman, me dit que le comportement est le meme avec debian unstable :

12:06 < erik> peut tu me donner le resultat de "$ aptitude show libstdc++6-4.3-dev | grep Version" ?
12:06 < Roman> Version : 4.3.5-1

Votre avis sur faire un bug report ?

godrik
godrik
Niveau 30
12 juillet 2010 à 18:45:31

mmm, on dirait que je touche un buffer systeme comme suggere par dnob.
si j'execute foo plusieurs fois, "schedule" n'apparait la premiere fois qu'apres un ctrl-c mais apparait immediatement al deuxieme fois.

On dirait que le probleme de buffer est dans mkfifo plutot...

godrik
godrik
Niveau 30
12 juillet 2010 à 21:55:14

bon, un update.
J'ai reecris le code en C et j'ai le meme probleme.
Considerer l'exemple suivant:

erik@powell:/tmp$ cat foo.c

  1. include <stdio.h>
  2. include <sys/types.h>
  3. include <sys/stat.h>
  4. include <fcntl.h>

int main ()
{
int fd = open("foo.in", O_WRONLY|O_APPEND,0);
write (fd, "schedule\n",9);
fsync(fd);
while(1);
return 0;
}
erik@powell:/tmp$ make foo
cc foo.c -o foo
erik@powell:/tmp$ mkfifo foo.in
erik@powell:/tmp$ tail -f foo.in &
[3] 5145
erik@powell:/tmp$ ./foo
^Cschedule

erik@powell:/tmp$ ./foo
schedule
^C
erik@powell:/tmp$

la premiere fois que j'appelle foo, rien n'apparait sur mon terminal jusqu'a ce que je tue foo avec ctrl-c, mais la deuxieme fois, tout fonctionne correctement.

Si je rajoute plus que une ligne dans foo.c, le comportement reste le meme. Je me demande donc si c'est un probleme de la fifo ou de tail maintenant.

Un avis ?

_skip
_skip
Niveau 10
12 juillet 2010 à 22:09:07

Je sais pas, à voir le code que j'ai posté, on voit bien l'appel à flush dans le pointeur de fonction endl.

Eventuellement tu pourrais mettre un point d'arrêt au débugger dans la fonction flush du template et exécuter les 2 codes pour être sûr que c'est bien la même fonction flush qui est appelée.
Maintenant bonne chance si t'emploies GDB parce que ce machin crashe sans arrête chez moi.

godrik
godrik
Niveau 30
12 juillet 2010 à 22:31:02

well, je ne compte pas gdbise le code puisque le meme probleme apparait avec un code C.
J'ai l'impression que le probleme vient plus soit du systeme (mkfifo) soit de tail -f...

un avis ?

dnob700
dnob700
Niveau 10
12 juillet 2010 à 22:59:48

Avec ton même fichier foo.in, et une petite implémentation de tail, voilà ce que j'obtiens :

$ cat b.c

  1. include <stdio.h>
  2. include <sys/types.h>
  3. include <sys/stat.h>
  4. include <fcntl.h>

int main ()
{
int fd = open("foo.in", O_RDONLY|O_APPEND);
char buf[10];
buf[9] = 0;
read(fd, buf,9);
write(1, buf, 9);
return 0;
}
$ ./b & ./a
[1] 12008
schedule
^C
[1]+ Done ./b

Donc il semble que le problème puisse effectivement venir de tail et non pas des tuyaux.

dnob700
dnob700
Niveau 10
13 juillet 2010 à 00:13:10

cela dit, c'est vrai qu'il est étrange que les deux appels successifs à foo ne produisent pas la même chose.

Si tu ne veux pas t'embêter, utilise des socket unix, le comportement est aussi prédictible que celui des socket tu as gratuitement la communication bidirectionnelle si tu en as besoin et, en prime, il sont plus rapide (particulièrement pour des petits messages), d'après des tests que j'ai fait aujourd'hui.

Sankukai
Sankukai
Niveau 10
14 juillet 2010 à 18:50:33

Avec ce code (ajout du close(fd)) :
cetcheve@scratchy$ cat foo.c

  1. include <stdio.h>
  2. include <sys/types.h>
  3. include <sys/stat.h>
  4. include <fcntl.h>

int main ()
{
int fd = open("foo.in", O_WRONLY|O_APPEND,0);
write (fd, "schedule\n",9);
fsync(fd);
close(fd);
while(1);
return 0;
}

ça fonctionne correctement chez moi (openbsd) :
cetcheve@scratchy$ mkfifo foo.in

cetcheve@scratchy$ tail -f foo.in &
[1] 30101

cetcheve@scratchy$ ./foo
schedule
^C

Je n'ai pas d'explication du pourquoi ça merdoie sans le close() uniquement la première fois (d'autant que ça fonctionne avec un fichier standard). L'implémentation standard de tail distingue les fichiers des pipes, il doit y avoir un truc particulier pour les fifos.

godrik
godrik
Niveau 30
14 juillet 2010 à 20:44:38

Merci sankukai pour ton message et pour le lien que tu m'as fait passe sur IRC ( http://lists.apple.com/archives/darwin-dev/2005/Mar/msg00102.html )

Pour l'instant mon code final fonctionne avec un ajout outrancier de flush(). Je passerais a des socket unix plus tard probablement. A cette occasion je ferais probablement un benchmark rapide des different moyen de communication inter processus.

dnob700
dnob700
Niveau 10
14 juillet 2010 à 22:08:55

C'est un peu idiot comme bench (je me suis posé cette question pour mon boulot en début de semaine, c'est ce que j'écrivais dans mes dernier message) :

http://repository.quare.fr/Caml/ipc_bench.ml
(c'est du ping entre deux processus qui s'échange des messages de différentes tailles).

sur ma machine ainsi que celle sur laquelle je travaille je vois un petit avantage pour les socket, particulièrement pour des petits messages. Mais je ne suis pas sûr que ce soit très significatif. En tout état de cause, je trouve les sockets plus faciles à utiliser.

À noter que les sockets et les pipes sont anonymes dans mon test, mais j'imagine que les implémentations nommées des deux sont exactement équivalentes.

Sous forums
  • Aide à l'achat Mac
  • Macintosh
  • Création de Jeux
  • Programmation
  • Création de sites web
  • Linux
  • Internet
  • Steam Deck
  • Hardware
La vidéo du moment