Насколько большой буфер канала?

Нажатие dd удалит ту строку (на самом деле, она сократит его). Таким образом, можно вставить его через p.

148
13.04.2017, 15:36
7 ответов

Мощность буфера канала варьируется через системы (и может даже варьироваться в той же системе). Я не уверен, что существует быстрый, легкий, и кросс-платформенный путь только к поиску способность канала.

Mac OS X, например, использует мощность 16 384 байтов по умолчанию, но может переключиться на 65 336-байтовые мощности, если большая запись сделана к каналу или переключится на мощность страницы единой системы, если слишком много памяти ядра уже используется буферами канала (см. xnu/bsd/sys/pipe.h, и xnu/bsd/kern/sys_pipe.c; так как это из FreeBSD, то же поведение может произойти там, также).

Один канал Linux (7) в странице справочника говорится, что способность канала составляет 65 536 байтов начиная с Linux 2.6.11 и страницы единой системы до того (например, 4 096 байтов в (32-разрядных) x86 системах). Код (include/linux/pipe_fs_i.h, и fs/pipe.c) кажется, использует 16 системных страниц (т.е. 64 кибибайта, если системная страница составляет 4 кибибайта), но буфер для каждого канала может быть скорректирован через fcntl на канале (до максимальной способности, через какие значения по умолчанию к 1 048 576 байтам, но может быть изменен /proc/sys/fs/pipe-max-size)).


Вот немного bash/perl комбинации, что я раньше тестировал способность канала в моей системе:

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

Вот то, что я нашел выполнение его с различными размерами записи на Mac OS X 10.6.7 системами (обратите внимание на изменения для записей, больше, чем 16 кибибитов):

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

/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

Примечание: PIPE_BUF значение определило в заголовочных файлах C (и pathconf оценивают за _PC_PIPE_BUF), не указывает способность каналов, но максимальное количество байтов, которые могут быть записаны атомарно (см. запись POSIX (2)).

Кавычка от 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.  */
146
27.01.2020, 19:28
  • 1
    Большой ответ. Специально для ссылки на запись POSIX (2), который говорит: эффективный размер канала или FIFO (максимальная сумма, которая может быть записана в одной операции, не блокируясь) может варьироваться динамично, в зависимости от реализации, таким образом, не возможно указать фиксированное значение для него. прием –  Mikel 25.04.2011, 08:59
  • 2
    Спасибо за упоминание fcntl() на Linux; я потратил некоторое время, ища программы буферизации пространства пользователя, потому что я думал, что встроенные каналы не имели достаточно большого буфера. Теперь я вижу, что они делают, если у меня есть CAP_SYS_RESOURCE, или корень готов развернуть максимальный размер канала. Когда то, что я хочу, будет только выполнено на определенном компьютере Linux (мой), это не должно быть проблемой. –  Daniel H 04.08.2013, 21:43
  • 3
    Можно ли объяснить основную идею о сценарии? Я уставился на него, и я не могу выяснить, как это работает? Особенно, что цель использует фигурные скобки здесь $ VAR= ({})?Спасибо. –  Wakan Tanka 12.03.2015, 02:24
  • 4
    @WakanTanka: Это - слишком для описания в комментарии, но что конкретная конструкция является присвоением параметра (var=…) из вывода замены команды ($(…)) это включает сгруппированные команды ({…}, и (…)). Это также использует несколько (менее общих) перенаправлений (т.е. 0<&- и 3>&1). –  Chris Johnsen 12.03.2015, 02:51
  • 5
    @WakanTanka: программа Perl пишет в свой stdout (созданный из оболочки канал — тот, который тестируется) в блоках данного размера и отчетов его stderr рабочее общее количество того, сколько это записало (пока это не получает ошибку — обычно, потому что буфер канала полон или возможно потому что конец чтения канала был закрыт после короткого времени (exec 0<&-)). Итоговый отчет собран (tail -1) и распечатанный наряду с размером записи. –  Chris Johnsen 12.03.2015, 03:27

эта строка оболочки может показать размер буфера канала также:

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

(отправляющий 1k разделяет на блоки к заблокированному каналу до полного буфера)... некоторые тестовые выводы:

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

самый короткий bash-one-liner, использующий printf:

M=0; while printf A; do >&2 printf "\r$((++M)) B"; done | sleep 999
34
27.01.2020, 19:28
  • 1
    Очень хороший! (dd if=/dev/zero bs=1 | sleep 999) & затем ожидайте секунда и killall -SIGUSR1 dd дает 65536 bytes (66 kB) copied, 5.4987 s, 11.9 kB/s - то же, поскольку Ваше решение, но в 1-байтовом решении ;) –  frostschutz 18.04.2013, 02:21
  • 2
    Для записи, на Солярисе 10/11 SPARC/x86 dd блоки команды на уровне 16 кибибайт. На Fedora 23/25 x86-64 это блокируется на уровне 64 кибибайт. –  maxschlepzig 01.01.2017, 14:08
  • 3
    @frostschutz: это - хорошее упрощение. Практично, Вы могли просто работать dd if=/dev/zero bs=1 | sleep 999 на переднем плане ожидайте секунда, затем нажмите ^C. Если Вы хотели остроту на Linux и BSD/macOS (более устойчивый, чем использование killall): dd if=/dev/zero bs=1 | sleep 999 & sleep 1 && pkill -INT -P $$ -x dd –  mklement0 23.01.2017, 20:39

Это быстрый и грязный взлом на 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
27.01.2020, 19:28

Вот несколько дополнительных альтернатив для изучения фактической мощности буфера труб, используя только команды оболочки:

# 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
27.01.2020, 19:28
$ ulimit -a | grep pipe
pipe size            (512 bytes, -p) 8

Итак, на моем Linux-компьютере по умолчанию установлено 8 * 512 = 4096 байтовых каналов.

Solaris и многие другие системы имеют аналогичную функцию ulimit.

0
27.01.2020, 19:28

Если вам нужно значение в Python>=3.3, вот простой метод (при условии, что вы можете выполнить вызов 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()))
0
27.01.2020, 19:28

Принятый ответ адаптирован кzsh:

#+begin_src bsh.dash :results verbatim :exports both :wrap example
test-buffer() {
    test $# -ge 1 || { echo "usage: $0 write-size [wait-time]"; return 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"
}
uname
for i in {1..18} ; do
    test-buffer $((2**i)) 1
done
#+end_src

Результаты:

#+begin_example
Darwin
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
#+end_example
-1
13.03.2021, 08:02

Теги

Похожие вопросы