Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Pipes et fin des commandes

7 réponses
Avatar
Antoine
Salut,

Quand je fais un:
% find / | head

qu'est-ce qui fait exactement que le find s'arrête ?


-- antoine

7 réponses

Avatar
Stephane CHAZELAS
2008-12-16, 11:07(+00), Antoine:
Salut,

Quand je fais un:
% find / | head

qu'est-ce qui fait exactement que le find s'arrête ?


[...]

head va faire un ou plusieurs read(0, buf, sizeof(buf)). Des
qu'il a trouvé 10 caracteres "NL" dans son input il va se
terminer, ce qui va fermer la sortie du pipe. A partir de ce
moment la, des que find va faire un write(1, ...), il va se
prendre un signal SIGPIPE qui va le tuer.

Donc:

1- pour qu'il meurt, il faut qu'il ecrive quelque chose apres
que head se termine, donc il peut ne pas se terminer tout de
suite s'il ne trouve pas de fichier.

2- find peut ecrire bien plus de 10 lignes avant d'etre tué,
parce que:
- dans un pipe, on peut ecrire pas mal avant que l'autre ne
lise (ya un buffer qui peut contenir plusieurs dixaines de kB
suivant les implementations), donc apres, c'est une histoire
de quel process est schedulé.
- "head" va lire morceau par morceau, les morceaux faisant
typiquement 4, 5 or 8 kB selon les implementation de stdio.
mais note que le read(0, ...) retournera mois que sizeof(buf)
si find n'en a pas ecrit autant au moment ou head fait appel a
read().
- surtout, find lui-meme va ecrire morceau par morceau puisque
son stdout n'est pas un terminal, donc ne va probablement rien
ecrire avant d'avoir trouver suffisamment de fichier pour
remplir un "morceau" de 4, 5 ou 8kB (stdio encore, il doit
faire des puts() ou printf()).


--
Stéphane
Avatar
Nicolas George
Stephane CHAZELAS wrote in message
:
- surtout, find lui-meme va ecrire morceau par morceau puisque
son stdout n'est pas un terminal, donc ne va probablement rien
ecrire avant d'avoir trouver suffisamment de fichier pour
remplir un "morceau" de 4, 5 ou 8kB (stdio encore, il doit
faire des puts() ou printf()).



Ça, ce n'est pas clair du tout. Si je devais écrire un find, je mettrais un
fflush après chaque nom de fichier, pour ne pas retarder la suite du
traitement. Et de fait, au moins GNU find se comporte ainsi.
Avatar
Stephane CHAZELAS
2008-12-16, 11:34(+00), Nicolas George:
Stephane CHAZELAS wrote in message
:
- surtout, find lui-meme va ecrire morceau par morceau puisque
son stdout n'est pas un terminal, donc ne va probablement rien
ecrire avant d'avoir trouver suffisamment de fichier pour
remplir un "morceau" de 4, 5 ou 8kB (stdio encore, il doit
faire des puts() ou printf()).



Ça, ce n'est pas clair du tout. Si je devais écrire un find, je mettrais un
fflush après chaque nom de fichier, pour ne pas retarder la suite du
traitement. Et de fait, au moins GNU find se comporte ainsi.



??? Au contraire, faire des fflush sur un pipe est suboptimal,
ca causerait plein de context-switch.

$ strace -e write find / | head
write(1, "/n/.journaln/mpl2n/binn/bin/chgrp"..., 4096/
/.journal
/mpl2
/bin
/bin/chgrp
/bin/busybox
/bin/gzip
/bin/bzexe
/bin/hostname
/bin/tartest
) = 4096
write(1, "0n/dev/.udev/names/sndx2fcontrol"..., 4096) = -1 EPIPE (Broken pipe)
--- SIGPIPE (Broken pipe) @ 0 (0) ---
+++ killed by SIGPIPE +++

~$ find --version
find (GNU findutils) 4.4.0
[...]

ltrace -Se fprintf,fflush
montre dans les 200 fprintf et pas un fflush et deux SYS_write
de 4096 octets le deuxieme causant un SIGPIPE.

--
Stéphane
Avatar
Antoine
> 2008-12-16, 11:07(+00), Antoine:
Salut,

Quand je fais un:
% find / | head

qu'est-ce qui fait exactement que le find s'arrête ?


[...]

head va faire un ou plusieurs read(0, buf, sizeof(buf)). Des qu'il a
trouvé 10 caracteres "NL" dans son input il va se terminer, ce qui va
fermer la sortie du pipe. A partir de ce moment la, des que find va
faire un write(1, ...), il va se prendre un signal SIGPIPE qui va le
tuer.



D'accord. Et admettons que je veuille regarder comment ceci est
implémenté par exemple sur mon système ( OpenBSD 4.4 GENERIC#1021 i386),
comment je peux trouver, à part un % grep SIGPIPE dans les sources ? Et
sys.tar.gz ou src.tar.gz ?

-- antoine
Avatar
Stephane Chazelas
2008-12-16, 11:50(+00), Antoine:
[...]
head va faire un ou plusieurs read(0, buf, sizeof(buf)). Des qu'il a
trouvé 10 caracteres "NL" dans son input il va se terminer, ce qui va
fermer la sortie du pipe. A partir de ce moment la, des que find va
faire un write(1, ...), il va se prendre un signal SIGPIPE qui va le
tuer.



D'accord. Et admettons que je veuille regarder comment ceci est
implémenté par exemple sur mon système ( OpenBSD 4.4 GENERIC#1021 i386),
comment je peux trouver, à part un % grep SIGPIPE dans les sources ? Et
sys.tar.gz ou src.tar.gz ?


[...]

C'est l'appel systeme pipe() appliqué a un fd ouvert sur un
pipe. Je ne connais pas les details d'implementation sur
OpenBSD, mais l'entry point dans le kernel de l'appel system
write(2) sera probablement dans sys.tar.gz. Ensuite, il est
probable que ca dispatche vers une fonction/methode "write"
specifique aux pipes, donc peut-etre dans un pipe.c. Il y aura
probablement un if (pipe.number_of_open_readers == 0) { ...;
send_SIGPIPE_to(current_thread); ...; }. Faire un
grep SIGPIPE est probablement un bon raccourci.

--
Stéphane
Avatar
Nicolas George
Stephane CHAZELAS wrote in message
:
??? Au contraire, faire des fflush sur un pipe est suboptimal,
ca causerait plein de context-switch.



find a de bonnes chances d'être intensif en I/O. Si la sortie de find est
rare et que la suite du pipeline est intensive en CPU, c'est un net gain de
temps de bufferiser par ligne.

~$ find --version
find (GNU findutils) 4.4.0
[...]



Oui, j'ai mal testé, au temps pour moi.

Ceci dit, je maintiens ma position de principe : j'ai horreur de ces
programmes orientés traitement de fichiers textes par ligne qui bufferisent
par bloc.
Avatar
Vincent Lefevre
Dans l'article <4947f916$0$27345$,
Nicolas George <nicolas$ écrit:

Stephane CHAZELAS wrote in message
:
> ??? Au contraire, faire des fflush sur un pipe est suboptimal,
> ca causerait plein de context-switch.



find a de bonnes chances d'être intensif en I/O. Si la sortie de find est
rare et que la suite du pipeline est intensive en CPU, c'est un net gain de
temps de bufferiser par ligne.



Je suis d'accord. Donc en gros, ça dépend du contexte. Dans l'idéal,
il serait bien que les fflush soient contrôlés par le temps (voire
au niveau du scheduler, mais ce n'est probablement pas possible).

Ceci dit, je maintiens ma position de principe : j'ai horreur de ces
programmes orientés traitement de fichiers textes par ligne qui
bufferisent par bloc.



Oui, surtout quand on a juste un filtre (e.g. de colorisation) et que
la sortie se fera tout de même sur le terminal.

Le problème s'est justement posé avec Subversion (e.g. svn log) et
a été corrigé.

De même que la commande après le pipe n'est pas censée bloquer les
écritures en ne lisant rien (si bien que le buffer devient plein).
C'est le problème de "less"...

--
Vincent Lefèvre - Web: <http://www.vinc17.org/>
100% accessible validated (X)HTML - Blog: <http://www.vinc17.org/blog/>
Work: CR INRIA - computer arithmetic / Arenaire project (LIP, ENS-Lyon)