Boru tamponu ne kadar büyük?


Yanıtlar:


142

Bir boru tamponunun kapasitesi sistemlere göre değişir (ve aynı sistemde bile değişebilir). Bir borunun kapasitesini aramak için hızlı, kolay ve çapraz platform bir yöntem olduğundan emin değilim.

Örneğin, Mac OS X, varsayılan olarak 16384 baytlık bir kapasite kullanır, ancak boruya büyük yazma işlemi yapılırsa 65336 baytlık kapasiteye geçebilir veya çok fazla çekirdek belleği zaten varsa, tek bir sistem sayfasının kapasitesine geçecektir boru tamponları tarafından kullanılıyor (bakınız xnu/bsd/sys/pipe.hve xnu/bsd/kern/sys_pipe.c; bunlar FreeBSD'den olduğundan, aynı davranış orada da olabilir).

Bir Linux boru (7) kılavuz sayfasında , boru kapasitesinin Linux 2.6.11'den beri 65536 bayt ve bundan önce tek bir sistem sayfası olduğunu söylüyor (örneğin (32-bit) x86 sistemlerinde 4096 bayt). Kod ( include/linux/pipe_fs_i.h, ve fs/pipe.c) , 16 sistem sayfasını kullanıyor gibi görünüyor (yani, bir sistem sayfası 4 KiB ise 64 KiB), ancak her borunun tamponu, borunun üzerindeki bir fcntl ile ayarlanabilir (varsayılan olarak 1048576 olan maksimum kapasiteye kadar). bayt, ancak üzerinden değiştirilebilir /proc/sys/fs/pipe-max-size)).


İşte sistemimde boru kapasitesini test etmek için kullandığım bir bash / perl kombinasyonu:

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

İşte Mac OS X 10.6.7 sisteminde çeşitli yazma boyutlarıyla çalıştırırken bulduğum şey (16KiB'den büyük yazma değişikliklerine dikkat edin):

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

Linux 3.19'daki aynı komut dosyası:

/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

Not: PIPE_BUFC başlık dosyalarında tanımlanan değer (ve bunun için pathconf değeri _PC_PIPE_BUF), boruların kapasitesini değil, atomik olarak yazılabilecek maksimum bayt sayısını belirtir (bkz. POSIX yazma (2) ).

Şundan alıntı 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.  */

14
Mükemmel cevap. Özellikle POSIX yazma (2) bağlantısı için: Demek ki: Bir borunun veya FIFO'nun (bir işlemde bloke etmeden yazılabilecek maksimum miktar) efektif boyutu uygulamaya bağlı olarak dinamik olarak değişebilir, bu nedenle mümkün değildir bunun için sabit bir değer belirtmek için.
Mikel

5
fcntl()Linux'ta bahsettiğiniz için teşekkür ederiz ; Kullanıcı arabellek programlarını ararken biraz zaman geçirdim, çünkü yerleşik boruların yeterince büyük bir arabellek içermediğini sanıyordum. Şimdi yaptıklarını görüyorum, eğer CAP_SYS_RESOURCE varsa ya da kök maksimum boru boyutunu genişletmeye istekliyse. İstediğim sadece belirli bir Linux bilgisayarda (benimki) çalıştırılacağı için, bu bir sorun olmamalı.
Daniel H,

1
Senaryonuzun temel fikrini açıklayabilir misiniz? Ona bakıyorum ve nasıl çalıştığını çözemiyorum? Özellikle burada parantez kullanarak amaç nedir VAR = $ ({})? Teşekkür ederim.
Wakan Tanka

@WakanTanka: Bu açıklamada tarif etmek için biraz fazla olmakla birlikte, bu özel yapı, bir bir parametre atama ( var=…bir çıktısının) komut ikamesi ( $(…)dahil) gruplandırılmış komutları ( {…}ve (…)). Aynı zamanda birkaç (daha az yaygın) yönlendirme kullanır (yani 0<&-ve 3>&1).
Chris Johnsen

2
@WakanTanka: Perl programı, belirli bir büyüklükteki bloklarda stdout'una (kabuk tarafından oluşturulan bir boru - test edilen olanı) yazar ve stderrine ne kadar yazıldığını (hata elde edene kadar) rapor eder. - genellikle borunun tamponu dolu olduğu için veya borunun okuma ucu kısa bir süre sonra kapandığından ( exec 0<&-)). Nihai rapor toplanır ( tail -1) ve yazı boyutuyla birlikte yazdırılır.
Chris Johnsen

33

bu kabuk satırı da boru tampon boyutunu gösterebilir:

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

(arabellek doluncaya kadar 1k blokajlı boruyu gönderir) ... bazı test çıktıları:

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

printf kullanarak en kısa bash-one-liner:

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

11
Çok hoş! (dd if=/dev/zero bs=1 | sleep 999) &daha sonra ikinci bekleyip killall -SIGUSR1 ddverir 65536 bytes (66 kB) copied, 5.4987 s, 11.9 kB/s); sizin çözüm olarak aynı fakat 1 bayt çözünürlükte -
frostschutz

2
Kayıt için, Solaris 10/11 SPARC / x86’da ddkomut 16 KiB’de kalıyor. Fedora 23/25 x86-64'te 64 KiB'de engeller.
maxschlepzig

1
@frostschutz: Güzel bir sadeleştirme. Pragmatik olarak, sadece dd if=/dev/zero bs=1 | sleep 999ön planda koşabilir , bir saniye bekleyebilir, sonra da basabilirsiniz ^C. Linux ve BSD / killalldd if=/dev/zero bs=1 | sleep 999 & sleep 1 && pkill -INT -P $$ -x dd
macOS'ta

7

Yalnızca kabuk komutlarını kullanarak gerçek boru tamponu kapasitesini keşfetmek için bazı alternatifler:

# 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

Solaris 10'da çıktıya uygun ancak 16 KiB ile aynı olmayan bloklarla aynı olan getconf PIPE_BUF /baskılar . 5120ulimit -a | grep pipedd .. | sleep ...
maxschlepzig

Fedora 25’de, ilk yesyönteminiz 73728dd if=/dev/zero bs=4096 status=none | pv -bn | sleep 1
maxschlepzig

6

Bu Ubuntu 12.04, YMMV'de hızlı ve kirli bir kesmek

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

0
$ ulimit -a | grep pipe
pipe size            (512 bytes, -p) 8

Bu yüzden Linux kutumda varsayılan olarak 8 * 512 = 4096 baytlık borum var.

Solaris ve diğer birçok sistem benzer ulimit fonksiyonuna sahiptir.


2
Bu (512 bytes, -p) 8, Fedora 23/25 ve 512 bytes, -p) 10Solaris 10'a yazdırır - ve bu değerler deneysel olarak bir engelleme ile türetilmiş arabellek boyutlarıyla eşleşmez dd.
maxschlepzig

0

Python> = 3.3'teki değere ihtiyacınız varsa, işte size basit bir yöntem (çağrıyı yapabileceğinizi varsayalım 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()))
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.