파이프 버퍼는 얼마나 큽니까?


146

에 주석으로 "| 진정한"나는 이유에 혼란 스러워요 메이크가하는 것과 같은 효과가 있습니다에서 "|| 사실" 사용자 CJM는 썼다 :

피해야 할 또 다른 이유 | true는 명령이 파이프 버퍼를 채우기에 충분한 출력을 생성하면 true를 읽기 위해 대기하는 것을 차단한다는 것입니다.

파이프 버퍼의 크기를 알 수있는 방법이 있습니까?

답변:


142

파이프 버퍼의 용량은 시스템마다 다르며 심지어 동일한 시스템에서도 달라질 수 있습니다. 파이프 용량을 조회하는 빠르고 쉽고 크로스 플랫폼 방법이 있는지 확실하지 않습니다.

예를 들어, Mac OS X은 기본적으로 16384 바이트의 용량을 사용하지만 파이프에 큰 쓰기를 수행하는 경우 65336 바이트 용량으로 전환하거나 너무 많은 커널 메모리가 이미있는 경우 단일 시스템 페이지의 용량으로 전환 할 수 있습니다 파이프 버퍼에 의해 사용됩니다 ( xnu/bsd/sys/pipe.hxnu/bsd/kern/sys_pipe.c;은 FreeBSD에서 왔기 때문에 동일한 동작이 발생할 수 있습니다).

한 Linux pipe (7) 매뉴얼 페이지에 따르면 파이프 용량은 Linux 2.6.11 이후 65536 바이트이고 그 이전의 단일 시스템 페이지 (예 : (32 비트) x86 시스템의 경우 4096 바이트)입니다. 코드 ( include/linux/pipe_fs_i.h, 및 fs/pipe.c)는 16 개의 시스템 페이지 (예 : 시스템 페이지가 4 KiB 인 경우 64 KiB)를 사용하는 것처럼 보이지만 각 파이프의 버퍼는 파이프의 fcntl 을 통해 조정할 수 있습니다 (기본 최대 용량은 1048576 임) 바이트이지만 /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 시스템에서 다양한 쓰기 크기로 실행 한 것으로 나타났습니다 (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

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_BUFC 헤더 파일 (및에 대한 pathconf_PC_PIPE_BUF)에 정의 된 값 은 파이프 용량을 지정하지 않고 원자 적으로 작성할 수있는 최대 바이트 수를 지정합니다 ( POSIX write (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.  */

14
좋은 대답입니다. 특히 POSIX write (2)에 대한 링크의 경우 : 파이프 또는 FIFO의 유효 크기 (차단없이 한 작업에서 쓸 수있는 최대량)는 구현에 따라 동적으로 다를 수 있으므로 불가능합니다. 고정 값을 지정하십시오.
Mikel

5
fcntl()Linux에 대해 언급 해 주셔서 감사합니다 . 내장 파이프에 충분한 버퍼가 없다고 생각했기 때문에 사용자 공간 버퍼링 프로그램을 찾는 데 시간을 보냈습니다. 이제 CAP_SYS_RESOURCE가 있거나 루트가 최대 파이프 크기를 확장하려는 경우 그들이하는 것을 알 수 있습니다. 내가 원하는 것은 특정 Linux 컴퓨터 (광산)에서만 실행될 것이기 때문에 이것은 문제가되지 않습니다.
Daniel H

1
스크립트의 기본 아이디어를 설명해 주시겠습니까? 나는 그것을 쳐다보고 있는데 어떻게 작동하는지 알 수 없습니까? VAR = $ ({}) 여기서 중괄호를 사용하는 목적은 무엇입니까? 감사합니다.
Wakan Tanka

@ WakanTanka : 주석에서 설명하는 것이 약간 있지만 특정 구성은 그룹화 된 명령 ( , 및 ) 을 포함 하는 명령 대체 ( ) 출력의 매개 변수 할당 ( var=…)입니다 . 또한 몇 가지 (흔하지 않은) 리디렉션 (예 : 및 )을 사용합니다. $(…){…}(…)0<&-3>&1
Chris Johnsen

2
@WakanTanka : Perl 프로그램은 주어진 크기의 블록 단위로 stdout (쉘 작성 파이프 – 테스트중인 파이프)에 기록하고 stderr에 기록 된 양을 기록합니다 (오류가 발생할 때까지) -일반적으로 파이프 버퍼가 가득 차거나 파이프의 판독 끝이 짧은 시간 후에 닫 혔기 때문일 수 있습니다 ( exec 0<&-)). 최종 보고서는 수집 tail -1크기 ( )와 함께 수집 및 인쇄됩니다.
Chris Johnsen

33

이 쉘 라인은 파이프 버퍼 크기도 표시 할 수 있습니다.

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.

printf를 사용하는 가장 짧은 bash-one-liner :

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

11
아주 좋아요! (dd if=/dev/zero bs=1 | sleep 999) &다음 잠깐 및 killall -SIGUSR1 dd65536 bytes (66 kB) copied, 5.4987 s, 11.9 kB/s) 솔루션과 동일하지만, 1 바이트 해상도 -
frostschutz

2
레코드의 경우 Solaris 10/11 SPARC / x86에서 dd명령이 16 KiB에서 차단됩니다. Fedora 23/25 x86-64에서는 64 KiB로 차단됩니다.
maxschlepzig

1
@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

7

다음은 쉘 명령 만 사용하여 실제 파이프 버퍼 용량을 탐색하는 몇 가지 추가 대안입니다.

# 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에서 getconf PIPE_BUF /출력 5120과 일치 ulimit -a | grep pipe하지만 어떤 dd .. | sleep ...블록 이후 16 KiB와 일치하지 않는 인쇄 .
maxschlepzig

Fedora 25에서 첫 번째 yes방법은 73728다음과 같이 결정된 64 KiB 대신에 인쇄 됩니다.dd if=/dev/zero bs=4096 status=none | pv -bn | sleep 1
maxschlepzig

6

우분투 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

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

따라서 Linux 상자에는 기본적으로 8 * 512 = 4096 바이트 파이프가 있습니다.

Solaris 및 기타 여러 시스템에는 유사한 ulimit 기능이 있습니다.


2
이것은 (512 bytes, -p) 8Fedora 23/25 및 512 bytes, -p) 10Solaris 10에서 인쇄 되며 이러한 값은 실험적으로 차단 된 버퍼 크기와 일치하지 않습니다 dd.
maxschlepzig

0

Python> = 3.3의 값이 필요한 경우 간단한 방법이 있습니다 (call out to을 가정 할 때 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()))
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.