web-dev-qa-db-fra.com

Quelle est la taille du tampon de tuyau?

Comme un commentaire dans je ne sais pas pourquoi "| true" dans un makefile a le même effet que "|| true" user cjm a écrit:

Une autre raison à éviter | true est que si la commande produisait suffisamment de sortie pour remplir le tampon de canal, elle bloquerait l'attente de true pour la lire.

Avons-nous un moyen de savoir quelle est la taille du tampon de tuyau?

150
Kit Sunde

La capacité d'un tampon de tuyau varie d'un système à l'autre (et peut même varier sur le même système). Je ne suis pas sûr qu'il existe un moyen rapide, facile et multiplateforme de simplement rechercher la capacité d'un tuyau.

Mac OS X, par exemple, utilise une capacité de 16384 octets par défaut, mais peut basculer vers des capacités de 65336 octets si une écriture importante est effectuée dans le canal, ou basculera vers une capacité d'une seule page système si trop de mémoire du noyau est déjà utilisé par les tampons de tuyau (voir xnu/bsd/sys/pipe.h et xnu/bsd/kern/sys_pipe.c ; puisque ceux-ci proviennent de FreeBSD, le même comportement peut se produire là aussi).

Un Linux pipe (7) page de manuel indique que la capacité du pipe est de 65536 octets depuis Linux 2.6.11 et un seul page système antérieure (par exemple 4096 octets sur les systèmes x86 (32 bits)). Le code ( include/linux/pipe_fs_i.h et fs/pipe.c ) semble utiliser 16 pages système (soit 64 Ko si une page système fait 4 Ko), mais le tampon de chaque pipe peut être ajusté via un fcntl sur la canalisation (jusqu'à une capacité maximale par défaut de 1048576 octets, mais peut être modifiée via /proc/sys/fs/pipe-max-size)).


Voici une petite bash / combinaison Perl que j'ai utilisée pour tester la capacité du tuyau sur mon système:

#!/bin/bash
test $# -ge 1 || { echo "usage: $0 write-size [wait-time]"; exit 1; }
test $# -ge 2 || set -- "$@" 1
bytes_written=$(
{
    exec 3>&1
    {
        Perl -e '
            $size = $ARGV[0];
            $block = q(a) x $size;
            $num_written = 0;
            sub report { print STDERR $num_written * $size, qq(\n); }
            report; while (defined syswrite STDOUT, $block) {
                $num_written++; report;
            }
        ' "$1" 2>&3
    } | (sleep "$2"; exec 0<&-);
} | tail -1
)
printf "write size: %10d; bytes successfully before error: %d\n" \
    "$1" "$bytes_written"

Voici ce que j'ai trouvé en l'exécutant avec différentes tailles d'écriture sur un système Mac OS X 10.6.7 (notez le changement pour les écritures supérieures à 16 Ko):

% /bin/bash -c 'for p in {0..18}; do /tmp/ts.sh $((2 ** $p)) 0.5; done'
write size:          1; bytes successfully before error: 16384
write size:          2; bytes successfully before error: 16384
write size:          4; bytes successfully before error: 16384
write size:          8; bytes successfully before error: 16384
write size:         16; bytes successfully before error: 16384
write size:         32; bytes successfully before error: 16384
write size:         64; bytes successfully before error: 16384
write size:        128; bytes successfully before error: 16384
write size:        256; bytes successfully before error: 16384
write size:        512; bytes successfully before error: 16384
write size:       1024; bytes successfully before error: 16384
write size:       2048; bytes successfully before error: 16384
write size:       4096; bytes successfully before error: 16384
write size:       8192; bytes successfully before error: 16384
write size:      16384; bytes successfully before error: 16384
write size:      32768; bytes successfully before error: 65536
write size:      65536; bytes successfully before error: 65536
write size:     131072; bytes successfully before error: 0
write size:     262144; bytes successfully before error: 0

Le même script sous Linux 3.19:

/bin/bash -c 'for p in {0..18}; do /tmp/ts.sh $((2 ** $p)) 0.5; done'
write size:          1; bytes successfully before error: 65536
write size:          2; bytes successfully before error: 65536
write size:          4; bytes successfully before error: 65536
write size:          8; bytes successfully before error: 65536
write size:         16; bytes successfully before error: 65536
write size:         32; bytes successfully before error: 65536
write size:         64; bytes successfully before error: 65536
write size:        128; bytes successfully before error: 65536
write size:        256; bytes successfully before error: 65536
write size:        512; bytes successfully before error: 65536
write size:       1024; bytes successfully before error: 65536
write size:       2048; bytes successfully before error: 65536
write size:       4096; bytes successfully before error: 65536
write size:       8192; bytes successfully before error: 65536
write size:      16384; bytes successfully before error: 65536
write size:      32768; bytes successfully before error: 65536
write size:      65536; bytes successfully before error: 65536
write size:     131072; bytes successfully before error: 0
write size:     262144; bytes successfully before error: 0

Noter la PIPE_BUF valeur définie dans les fichiers d'en-tête C (et la valeur pathconf pour _PC_PIPE_BUF), ne spécifie pas la capacité des canaux, mais le nombre maximum d'octets qui peuvent être écrits atomiquement (voir POSIX write (2) ).

Citation de include/linux/pipe_fs_i.h :

/* Differs from PIPE_BUF in that PIPE_SIZE is the length of the actual
   memory allocation, whereas PIPE_BUF makes atomicity guarantees.  */
147
Chris Johnsen

cette ligne Shell peut également afficher la taille de la mémoire tampon du tuyau:

M=0; while true; do dd if=/dev/zero bs=1k count=1 2>/dev/null; \
       M=$(($M+1)); echo -en "\r$M KB" 1>&2; done | sleep 999

(envoi de 1 000 morceaux vers un canal bloqué jusqu'à ce que le tampon soit plein) ... quelques sorties de test:

64K (intel-debian), 32K (aix-ppc), 64K (jslinux bellard.org)      ...Ctrl+C.

bash-one-liner le plus court utilisant printf:

M=0; while printf A; do >&2 printf "\r$((++M)) B"; done | sleep 999
36
Asain Kujovic

Voici d'autres alternatives pour explorer la capacité réelle de la mémoire tampon de tuyau à l'aide des commandes Shell uniquement:

# get pipe buffer size using Bash
yes produce_this_string_as_output | tee >(sleep 1) | wc -c

# portable version
( (sleep 1; exec yes produce_this_string_as_output) & echo $! ) | 
     (pid=$(head -1); sleep 2; kill "$pid"; wc -c </dev/stdin)

# get buffer size of named pipe
sh -c '
  rm -f fifo
  mkfifo fifo
  yes produce_this_string_as_output | tee fifo | wc -c &
  exec 3<&- 3<fifo
  sleep 1
  exec 3<&-
  rm -f fifo
'

# Mac OS X
#getconf PIPE_BUF /
#open -e /usr/include/limits.h /usr/include/sys/pipe.h
# PIPE_SIZE
# BIG_PIPE_SIZE
# SMALL_PIPE_SIZE
# PIPE_MINDIRECT
8
chan

Ceci est un hack rapide et sale sur Ubuntu 12.04, YMMV

cat >pipesize.c

#include <unistd.h>
#include <errno.h>
#include </usr/include/linux/fcntl.h>
#include <stdio.h>

void main( int argc, char *argv[] ){
  int fd ;
  long pipesize ;

  if( argc>1 ){
  // if command line arg, associate a file descriptor with it
    fprintf( stderr, "sizing %s ... ", argv[1] );
    fd = open( argv[1], O_RDONLY|O_NONBLOCK );
  }else{
  // else use STDIN as the file descriptor
    fprintf( stderr, "sizing STDIN ... " );
    fd = 0 ;
  }

  fprintf( stderr, "%ld bytes\n", (long)fcntl( fd, F_GETPIPE_SZ ));
  if( errno )fprintf( stderr, "Uh oh, errno is %d\n", errno );
  if( fd )close( fd );
}

gcc -o pipesize pipesize.c

mkfifo /tmp/foo

./pipesize /tmp/foo

>sizing /tmp/foo ... 65536 bytes

date | ./pipesize

>sizing STDIN ... 65536 bytes
6
Jeff

Si vous avez besoin de la valeur en Python> = 3.3, voici une méthode simple (en supposant que vous pouvez exécuter l'appel à dd):

from subprocess import Popen, PIPE, TimeoutExpired
p = Popen(["dd", "if=/dev/zero", "bs=1"], stdin=PIPE, stdout=PIPE)
try: 
    p.wait(timeout=1)
except TimeoutExpired: 
    p.kill()
    print(len(p.stdout.read()))
1
unhammer
$ ulimit -a | grep pipe
pipe size            (512 bytes, -p) 8

Donc, sur ma boîte Linux, j'ai 8 * 512 = 4096 octets par défaut.

Solaris et de nombreux autres systèmes ont une fonction ulimit similaire.

0
Sam Watkins