it-swarm-id.com

Seberapa besar penyangga pipa?

Sebagai komentar di Saya bingung mengapa "| true" di makefile memiliki efek yang sama dengan "|| true" pengguna cjm wrote:

Alasan lain untuk menghindari | true adalah bahwa jika perintah menghasilkan output yang cukup untuk mengisi buffer pipa, itu akan memblokir menunggu true untuk membacanya.

Apakah kita punya cara untuk mengetahui ukuran buffer pipa itu?

150
Kit Sunde

Kapasitas penyangga pipa bervariasi antar sistem (dan bahkan dapat bervariasi pada sistem yang sama). Saya tidak yakin ada cara cepat, mudah, dan lintas platform untuk hanya mencari kapasitas pipa.

Mac OS X, misalnya, menggunakan kapasitas 16384 byte secara default, tetapi dapat beralih ke kapasitas 65336 byte jika penulisan besar dilakukan ke pipa, atau akan beralih ke kapasitas halaman sistem tunggal jika terlalu banyak memori kernel sudah sedang digunakan oleh penyangga pipa (lihat xnu/bsd/sys/pipe.h , dan xnu/bsd/kern/sys_pipe.c ; karena ini dari FreeBSD, perilaku yang sama dapat terjadi di sana juga).

Satu Linux pipa (7) halaman manual mengatakan bahwa kapasitas pipa adalah 65536 byte sejak Linux 2.6.11 dan satu halaman sistem tunggal sebelum itu (mis. 4096 bytes pada (x-bit) sistem x86). Kode ( include/linux/pipe_fs_i.h , dan fs/pipe.c ) tampaknya menggunakan 16 halaman sistem (yaitu 64 KiB jika halaman sistem adalah 4 KiB), tetapi buffer untuk setiap pipa dapat disesuaikan melalui fcntl pada pipa (hingga kapasitas maksimum yang standarnya adalah 1048576 byte, tetapi dapat diubah melalui /proc/sys/fs/pipe-max-size)).


Berikut adalah kombinasi bash kecil / Perl yang saya gunakan untuk menguji kapasitas pipa di sistem saya:

#!/bin/bash
test $# -ge 1 || { echo "usage: $0 write-size [wait-time]"; exit 1; }
test $# -ge 2 || set -- "[email protected]" 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"

Inilah yang saya temukan menjalankannya dengan berbagai ukuran tulis pada sistem Mac OS X 10.6.7 (perhatikan perubahan untuk penulisan yang lebih besar dari 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

Skrip yang sama di 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

Catatan: PIPE_BUF nilai yang ditentukan dalam file header C (dan pathconf nilai untuk _PC_PIPE_BUF), tidak menentukan kapasitas pipa, tetapi jumlah maksimum byte yang dapat ditulis secara atom (lihat POSIX tulis (2) ).

Kutipan dari 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

shell-line ini juga dapat menunjukkan ukuran buffer pipa:

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

(mengirim potongan 1k ke pipa yang diblokir sampai buffer penuh) ... beberapa hasil uji:

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

bash-one-liner terpendek menggunakan printf:

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

Berikut adalah beberapa alternatif lebih lanjut untuk mengeksplorasi kapasitas buffer pipa yang sebenarnya menggunakan perintah Shell saja:

# 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

Ini adalah retasan cepat dan kotor di 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

Jika Anda membutuhkan nilai dalam Python> = 3.3, berikut ini adalah metode sederhana (dengan asumsi Anda dapat menjalankan panggilan keluar ke 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

Jadi pada kotak Linux saya, saya memiliki 8 * 512 = 4096 byte pipa secara default.

Solaris dan banyak sistem lainnya memiliki fungsi ulimit yang serupa.

0
Sam Watkins