使用管道和此处字符串的资源使用情况

utl*_*amn 20 bash pipe resources

我们可以使用以下两个中获得相同的结果bash

echo 'foo' | cat
Run Code Online (Sandbox Code Playgroud)

cat <<< 'foo'
Run Code Online (Sandbox Code Playgroud)

我的问题是,就所使用的资源而言,这两者之间有什么区别,哪个更好?

我的想法是,在使用管道时,我们使用了一个额外的进程echo和管道,而在此字符串中,只有一个文件描述符与cat.

mik*_*erv 18

管道是在内核文件系统中打开的文件,不能作为磁盘上的常规文件访问。它仅自动缓冲到特定大小,并在满时最终阻塞。与块设备上的文件不同,管道的行为与字符设备非常相似,因此通常不支持lseek()并且从它们读取的数据无法像使用常规文件那样再次读取。

here-string 是在挂载的文件系统中创建的常规文件。shell 创建文件并保留其描述符,同时在向文件写入字节/从文件读取字节之前立即删除其唯一的文件系统链接(并因此删除它)。内核将维护文件所需的空间,直到所有进程为其释放所有描述符。如果从这样的描述符读取的孩子有能力这样做,它可以倒带lseek()并再次读取。

在这两种情况下,标记<<<|代表文件描述符,而不一定是文件本身。您可以通过执行以下操作来更好地了解正在发生的事情:

readlink /dev/fd/1 | cat
Run Code Online (Sandbox Code Playgroud)

...或者...

ls -l <<<'' /dev/fd/*
Run Code Online (Sandbox Code Playgroud)

这两个文件之间最显着的区别在于,here-string/doc 几乎是一次性的——shell 在向子进程提供读取描述符之前将所有数据写入其中。另一方面,外壳在适当的描述符上打开管道并分叉子级来管理管道的那些 - 因此它在两端同时写入/读取。

然而,这些区别只是普遍适用。据我所知(实际上并不是那么远)这对于几乎所有处理<<<here-string 简写以<<进行 here-document 重定向的shell 都是如此,只有yash. yash, busybox, dash, 和其他ash变体确实倾向于支持带有管道的 here-documents,因此在这些 shell 中,毕竟两者之间确实几乎没有区别。

好的 - 两个例外。现在我正在考虑它,ksh93实际上根本没有为 做管道|,而是通过套接字处理整个业务 - 尽管它确实<<<*像大多数其他人一样执行了已删除的 tmp 文件。更重要的是,它只将管道的单独部分放在子外壳环境中,这是一种 POSIX 委婉说法,至少它的作用类似于子外壳,因此甚至不进行分叉。

事实是,@PSkocik 的基准测试(非常有用)的结果可能因多种原因而有很大差异,其中大部分都依赖于实现。对于此处的文档设置,最大的因素将是目标${TMPDIR}文件系统类型和当前缓存配置/可用性,以及要写入的数据量。对于管道,它将是 shell 进程本身的大小,因为为所需的分叉制作了副本。这种方式在管道设置中bash糟糕(包括$(命令)替换) - 因为它很大而且慢,但ksh93它几乎没有任何区别。

这是另一个小 shell 片段,用于演示 shell 如何拆分管道的子 shell:

readlink /dev/fd/1 | cat
Run Code Online (Sandbox Code Playgroud)
32059  #bash's pid
32059  #sh's ppid
32059  #1st subshell's $$
32111  #1st subshell sh's ppid
32059  #2cd subshell's $$
32114  #2cd subshell sh's ppid
Run Code Online (Sandbox Code Playgroud)

流水线pipe_who()调用报告的内容与当前 shell 中运行的报告之间的差异是由于(子 shell 的)指定行为,即在$$扩展时声明父 shell 的 pid 。尽管bash子 shell 肯定是单独的进程,但$$特殊的 shell 参数并不是此信息的可靠来源。尽管如此,子shell的子shshell不会拒绝准确报告其$PPID.

  • @utlamn - 实际上,是的 - 只是 [pipefs](http://www.linux.org/threads/pipefs-sockfs-debugfs-and-securityfs.5383/)。这一切都在内核中 - 但是*(除了像 FUSE 这样的东西)* [*all* file i/o](http://www.tldp.org/LDP/lki/lki-3.html)也是如此。 (2认同)

PSk*_*cik 11

基准测试无可替代:

pskocik@ProBook:~ 
$ time (for((i=0;i<1000;i++)); do cat<<< foo >/dev/null; done  )

real    0m2.080s
user    0m0.738s
sys 0m1.439s
pskocik@ProBook:~ 
$ time (for((i=0;i<1000;i++)); do echo foo |cat >/dev/null; done  )

real    0m4.432s
user    0m2.095s
sys 0m3.927s
$ time (for((i=0;i<1000;i++)); do cat <(echo foo) >/dev/null; done  )
real    0m3.380s
user    0m1.121s
sys 0m3.423s
Run Code Online (Sandbox Code Playgroud)

对于大量数据:

TENMEG=$(ruby -e 'puts "A"*(10*1024*1024)')
pskocik@ProBook:~ 
$ time (for((i=0;i<100;i++)); do echo "$TENMEG" |cat >/dev/null; done  )

real    0m42.327s
user    0m38.591s
sys 0m4.226s
pskocik@ProBook:~ 
$ time (for((i=0;i<100;i++)); do cat<<< "$TENMEG" >/dev/null; done  )

real    1m26.946s
user    1m23.116s
sys 0m3.681s
pskocik@ProBook:~ 

$ time (for((i=0;i<100;i++)); do cat <(echo "$TENMEG") >/dev/null; done  )

real    0m43.910s
user    0m40.178s
sys 0m4.119s
Run Code Online (Sandbox Code Playgroud)

管道版本的设置成本似乎更高,但最终效率更高。

  • `echo foo &gt;/dev/shm/1;cat /dev/shm/1 &gt;/dev/null` 在这两种情况下似乎都很快...... (2认同)