Qual é o tamanho do buffer do pipe?

129

Como comentário em Estou confuso sobre o porquê" | true "em um makefile tem o mesmo efeito que" || true " user cjm escreveu:

Another reason to avoid | true is that if the command produced enough output to fill up the pipe buffer, it would block waiting for true to read it.

Temos alguma maneira de descobrir qual é o tamanho do buffer de tubo?

    
por Kit Sunde 25.04.2011 / 01:11

6 respostas

130

A capacidade de um buffer de tubulação varia entre os sistemas (e pode até variar no mesmo sistema). Não tenho certeza se existe uma maneira rápida, fácil e de plataforma cruzada de apenas pesquisar a capacidade de um cano.

O Mac OS X, por exemplo, usa uma capacidade de 16384 bytes por padrão, mas pode alternar para capacidades de 65336 bytes se grandes gravações forem feitas no canal, ou alternará para uma capacidade de uma única página do sistema se houver muito kernel a memória já está sendo usada por buffers de pipe (consulte xnu/bsd/sys/pipe.h e xnu/bsd/kern/sys_pipe.c ; estes são do FreeBSD, o mesmo comportamento pode acontecer lá também.

Uma canal () diz que a capacidade do canal é 65536 bytes desde o Linux 2.6.11 e uma única página do sistema antes disso (por exemplo, 4096 bytes em sistemas x86 de 32 bits). O código ( include/linux/pipe_fs_i.h e fs/pipe.c ) parece usar 16 páginas do sistema (isto é, 64 KiB se uma página do sistema for 4 KiB), mas o buffer para cada pipe pode ser ajustado através de um fcntl no pipe (até uma capacidade máxima que padrão é 1048576 bytes, mas pode ser alterada via /proc/sys/fs/pipe-max-size )).

Aqui está uma pequena combinação bash / perl que usei para testar a capacidade do pipe no meu sistema:

#!/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"

Aqui está o que eu encontrei executando com vários tamanhos de gravação em um sistema Mac OS X 10.6.7 (observe a mudança para gravações maiores que 16KiB):

% /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

O mesmo script no 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

Nota: O valor PIPE_BUF definido nos arquivos de cabeçalho C (e o valor pathconf para _PC_PIPE_BUF ), não especifica a capacidade dos canais, mas o número máximo de bytes que podem ser escrito atomicamente (veja POSIX escreva (2) ) .

Cite 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.  */
    
por 25.04.2011 / 07:32
30

este shell-line também pode mostrar o tamanho do buffer do pipe:

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

(enviando 1k pedaços para o canal bloqueado até o buffer ficar cheio) ... algumas saídas de teste:

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

short-bash-one-liner usando printf:

M=0; while printf A; do >&2 printf "\r$((++M)) B"; done | sleep 999
    
por 18.04.2013 / 00:52
7

Aqui estão algumas alternativas adicionais para explorar a capacidade real do buffer de tubulação usando somente comandos shell:

# 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
    
por 28.10.2014 / 15:16
6

Este é um hack rápido e sujo no 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
    
por 11.09.2014 / 04:06
0
$ ulimit -a | grep pipe
pipe size            (512 bytes, -p) 8

Portanto, na minha caixa de Linux, eu tenho 8 * 512 = 4096 bytes por padrão.

O Solaris e muitos outros sistemas têm uma função ulimit semelhante.

    
por 16.12.2016 / 12:58
0

Se você precisa do valor em Python > = 3.3, aqui está um método simples (supondo que você pode executar chamadas para 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()))
    
por 22.03.2017 / 14:52

Tags