作为注释,我对为什么makefile中的“ | true”与“ || true”用户cjm 产生相同的效果感到困惑:
避免的另一个原因 true是如果命令产生的输出足以填充管道缓冲区,它将阻塞等待true的读取。
我们是否可以找出管道缓冲区的大小?
作为注释,我对为什么makefile中的“ | true”与“ || true”用户cjm 产生相同的效果感到困惑:
避免的另一个原因 true是如果命令产生的输出足以填充管道缓冲区,它将阻塞等待true的读取。
我们是否可以找出管道缓冲区的大小?
Answers:
管道缓冲区的容量在不同系统之间有所不同(甚至在同一系统上可能有所不同)。我不确定是否有一种快速,轻松和跨平台的方法来查找管道的容量。
例如,Mac OS X默认使用16384字节的容量,但是如果对管道进行大量写入,则可以切换到65336字节的容量,如果已经有太多的内核内存,则可以切换到单个系统页面的容量。由管道缓冲区使用(请参阅xnu/bsd/sys/pipe.h
和xnu/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_BUF
C头文件中定义的值(以及的pathconf值_PC_PIPE_BUF
)没有指定管道的容量,而是指定了可以自动写入的最大字节数(请参阅POSIX write(2))。
/* Differs from PIPE_BUF in that PIPE_SIZE is the length of the actual
memory allocation, whereas PIPE_BUF makes atomicity guarantees. */
fcntl()
在Linux上的提及;我花了一段时间寻找用户空间缓冲程序,因为我认为内置管道没有足够大的缓冲。现在,如果我有CAP_SYS_RESOURCE或root愿意扩展最大管道大小,我就会看到它们的作用。因为我想要的只能在特定的Linux计算机上运行(我的),所以这应该不是问题。
exec 0<&-
)之后已关闭。收集最终报告(tail -1
),并与写入大小一起打印。
此shell行也可以显示管道缓冲区的大小:
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
(dd if=/dev/zero bs=1 | sleep 999) &
然后等待一秒钟,并killall -SIGUSR1 dd
给出65536 bytes (66 kB) copied, 5.4987 s, 11.9 kB/s
-与您的解决方案相同,但分辨率为1字节;)
dd
命令在16 KiB处阻塞。在Fedora 23/25 x86-64上,它以64 KiB阻塞。
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
以下是一些仅使用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
getconf PIPE_BUF /
打印。5120
ulimit -a | grep pipe
dd .. | sleep ...
yes
一种方法会打印,73728
而不是用dd if=/dev/zero bs=4096 status=none | pv -bn | sleep 1
这是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
$ ulimit -a | grep pipe
pipe size (512 bytes, -p) 8
因此,在我的Linux机器上,默认情况下,我有8 * 512 = 4096字节的管道。
Solaris和许多其他系统具有类似的ulimit功能。
(512 bytes, -p) 8
在Fedora 23/25和512 bytes, -p) 10
Solaris 10上打印-并且这些值与通过阻塞实验得出的缓冲区大小不匹配dd
。