Pourquoi les pipes « se bloquent » : le buffering
- Description du problème : quand on exécute la commande
tail -f /some/log/file | grep thing1 | grep thing2 pour trouver une sortie précise dans un fichier de log, il peut arriver que rien ne s’affiche si les lignes sont ajoutées lentement. On a alors l’impression que le pipe est bloqué, mais en réalité le programme n’écrit tout simplement pas encore les données dans le pipe.
Cause du buffering
- Pourquoi il existe : il est courant qu’un programme bufferise les données avant de les écrire dans un pipe ou un fichier. C’est pour des raisons de performance : au lieu d’écrire immédiatement chaque sortie, il accumule une certaine quantité de données puis les écrit d’un coup.
- Exemple :
grep thing1 peut conserver les lignes correspondantes jusqu’à avoir accumulé 8 KB de données, ce qui peut retarder l’affichage.
Pas de buffering lors de l’écriture vers le terminal
- Différence entre terminal et pipe :
grep utilise un buffering par ligne lorsque la sortie va vers un terminal, mais un buffering par blocs lorsqu’elle va vers un pipe. Ce comportement est déterminé via la fonction isatty.
Commandes qui bufferisent et celles qui ne le font pas
- Commandes non bufferisées :
tail, cat, tee, etc. ne bufferisent pas.
- Commandes bufferisées :
grep, sed, awk, tcpdump, jq, tr, cut, etc. bufferisent, et certaines permettent de désactiver ce comportement avec un flag spécifique.
Buffering de sortie par défaut dans les langages de programmation
- Langages concernés : C, Python, Ruby, Perl, etc. bufferisent la sortie par défaut, avec des moyens spécifiques pour désactiver ce comportement.
Perte du contenu du buffer quand on appuie sur Ctrl-C
- Description du problème : lorsqu’on appuie sur
Ctrl-C, le contenu du buffer est perdu. Cela arrive parce que le signal SIGINT est envoyé en premier.
- Solution : trouver le PID de
tcpdump puis exécuter kill -TERM $PID permet de vider le buffer.
Buffering aussi lors d’une redirection vers un fichier
- Redirection vers un fichier : il y a aussi du buffering lors d’une redirection vers un fichier, mais sans le problème de perte du buffer causé par
Ctrl-C.
Plusieurs façons d’éviter le buffering
- Solution 1 : exécuter un programme qui se termine rapidement.
- Solution 2 : utiliser le flag
--line-buffered de grep.
- Solution 3 : utiliser
awk.
- Solution 4 : utiliser
stdbuf.
- Solution 5 : utiliser
unbuffer.
Variables d’environnement pour désactiver le buffering
- Idée : il serait utile d’avoir une variable d’environnement standard comme
PYTHON_UNBUFFERED. Une variable comme NO_BUFFER est proposée.
Contenu omis
- Sujets omis : la différence entre buffering par ligne et absence totale de buffering, la différence de buffering entre stderr et stdout, le buffering du pilote TTY du système d’exploitation, etc.
1 commentaires
Avis Hacker News
Un accès avec buffering doit être vidé après un certain nombre d’octets ou un certain délai. C’est une manière courante de résoudre des problèmes similaires dans les interfaces matérielles
Si le CPU de tout le système devient inactif, on voudrait vider tous les buffers
Je travaille avec des systèmes NIX depuis plus de 20 ans, mais j’oublie toujours les problèmes de buffering
J’utilise Unix depuis plus de 35 ans, mais je n’avais jamais complètement compris le fonctionnement du buffering. Cette explication m’a été utile
Il y a une confusion entre « non bufferisé » et « bufferisé par ligne »
Les buffers existent parce qu’écrire dans un buffer est relativement beaucoup plus lent qu’afficher la sortie à l’écran
Appuyer sur Ctrl-C peut entraîner la perte du contenu du buffer
J’ai eu des problèmes de buffering sous Unix, et toutes les implémentations de
awkne se comportent pas de la même manièreJ’ai l’impression d’avoir raté la blague sur le pipeline gelé