作为一个注释,我对为什么makefile中的“ | true”与“ || true”具有相同的作用感到困惑,用户cjm写道:
如果命令产生的输出足以填满管道缓冲区,它将阻塞等待true的读取。


我们是否可以通过某种方式找出管道的大小缓冲区是?

#1 楼

管道缓冲区的容量在不同系统之间有所不同(甚至在同一系统上可能有所不同)。我不确定是否有一种快速,简便和跨平台的方法来查找管道的容量。例如,Mac OS X默认使用16384字节的容量,但是可以如果对管道进行大量写入,则切换到65336字节的容量;如果管道缓冲区已经使用过多的内核内存,则切换到单个系统页面的容量(请参阅xnu/bsd/sys/pipe.hxnu/bsd/kern/sys_pipe.c;由于它们来自FreeBSD,

一个Linux pipe(7)手册页指出,自Linux 2.6.11起,管道容量为65536字节,而在此之前的单个系统页(例如4096字节) (在32位x86系统上)。代码(include/linux/pipe_fs_i.hfs/pipe.c)似乎使用16个系统页面(即,如果系统页面为4 KiB,则为64 KiB),但是可以通过管道上的fcntl调整每个管道的缓冲区(最大容量为默认值到1048576字节,但可以通过/proc/sys/fs/pipe-max-size来更改))。


这里有一些bash / perl组合,用于测试系统上的管道容量:

#!/bin/bash
test $# -ge 1 || { echo "usage: 
% /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
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; } ' "" 2>&3 } | (sleep ""; exec 0<&-); } | tail -1 ) printf "write size: %10d; bytes successfully before error: %d\n" \ "" "$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: 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


Linux 3.19上的相同脚本:

/* Differs from PIPE_BUF in that PIPE_SIZE is the length of the actual
   memory allocation, whereas PIPE_BUF makes atomicity guarantees.  */



注意:C头文件中定义的PIPE_BUF值(和_PC_PIPE_BUF的pathconf值)没有指定管道的容量,而是指定了可以原子写入的最大字节数(请参阅POSIX write(2))。 />
q4312078q

评论


好答案。尤其是指向POSIX write(2)的链接,它表示:管道或FIFO的有效大小(一次操作中可以无阻塞地写入的最大数量)可能会根据实现而动态变化,因此不可能为它指定一个固定值。

– Mikel
2011年4月25日下午5:59

感谢您在Linux上提及fcntl();我花了一段时间寻找用户空间缓冲程序,因为我认为内置管道没有足够大的缓冲。现在,如果我有CAP_SYS_RESOURCE或root愿意扩展最大管道大小,我会看到它们的作用。因为我想要的只能在特定的Linux计算机上运行(我的),所以这应该不是问题。

–丹尼尔·H
13年8月4日在18:43

您能解释一下脚本的基本概念吗?我盯着它看,我不知道它是如何工作的?特别是在这里使用大括号VAR = $({})的目的是什么?谢谢。

–瓦坎坦卡
15年3月12日在0:24

@WakanTanka:注释中有很多描述,但是特定的结构是命令替换($(…))的输出的参数赋值(var =…),其中包括分组的命令({…}),以及(...))。它还使用几个(不太常见的)重定向(即0 <&-和3>&1)。

–克里斯·约翰森(Chris Johnsen)
15 Mar 12 '15 at 0:51

@WakanTanka:Perl程序以给定大小的块写入其stdout(一个由外壳创建的管道,正在测试的管道),并向stderr报告其已写入的运行总数(直到收到错误)。 —通常是因为管道的缓冲区已满,或者可能是因为管道的读取端在短时间(执行0 <&-)之后已关闭。收集最终报告(尾号-1),并与写入大小一起打印。

–克里斯·约翰森(Chris Johnsen)
15年3月12日在1:27

#2 楼

该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


使用printf的最短bash-one-liner:

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


评论


非常好! (dd if = / dev / zero bs = 1 |睡眠999)然后等待一秒钟然后killall -SIGUSR1 dd复制65536字节(66 kB),5.4987 s,11.9 kB / s-与您的解决方案相同,但为1字节分辨率;)

–frostschutz
13年4月17日在23:21

作为记录,在Solaris 10/11 SPARC / x86上,dd命令在16 KiB处阻塞。在Fedora 23/25 x86-64上,它以64 KiB阻塞。

–maxschlepzig
17年1月1日在12:08

@frostschutz:这是一个很好的简化。实用上,您可以只运行dd if = / dev / zero bs = 1 |将999放在前台,等待一秒钟,然后按^ C。如果您想要在Linux和BSD / macOS上使用单线(比使用killall更强大):dd if = / dev / zero bs = 1 |睡眠999&睡眠1 &&杀死-INT -P $$ -x dd

–mklement0
17年1月23日在18:39

#3 楼

以下是一些仅使用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


评论


在Solaris 10上,getconf PIPE_BUF /打印5120与ulimit -a |相匹配。 grep管道输出,但与之后的dd .. |不匹配的16 KiB睡觉...块。

–maxschlepzig
17年1月1日在12:16

在Fedora 25上,您的第一个yes方法将打印73728,而不是由dd if = / dev / zero bs = 4096 status = none确定的64 KiB。 pv -bn |睡1

–maxschlepzig
17年1月1日在12:30

#4 楼

这是在Ubuntu 12.04,YMMV上的快速而肮脏的hack

 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
 


#5 楼

如果您需要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()))


#6 楼

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


因此,在我的Linux机器上,默认情况下,我有8 * 512 = 4096字节的管道。

评论


在Fedora 23/25上打印(512字节,-p)为8,在Solaris 10上打印512字节,-p)为10-且这些值与通过阻塞dd实验得出的缓冲区大小不匹配。

–maxschlepzig
17年1月1日在12:13

我在Debian 10上也得到了(512字节,-p)8。让我想知道此值实际上描述了什么。

–索科维
20 Jun 24'9:10



ulimit显示权限。

–天花板
20年11月4日在8:56