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的子sh
shell不会拒绝准确报告其$PPID
.
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)
管道版本的设置成本似乎更高,但最终效率更高。