给定
cmd='fun(){ echo "$@"; }; fun $(fun $(fun hi))'
shell 往往需要创建 2 个叉子才能实现
strace-f(){ strace -f "$@" 2>&1; };
for sh in dash bash zsh ksh; do
printf "$sh\t" ; strace-f $sh -c "$cmd" |grep -e clone -e fork -c;
done
除了ksh
英勇地做到了一次而没有分叉:
dash 2
bash 2
zsh 2
ksh 0
它是如何做到的?
编辑:
以下是插入管子后的情况:
cmd='fun(){ echo "$@"| echo "$@"; }; fun $(fun $(fun hi))'
输出:
dash 11
bash 10
zsh 5
ksh 3
答案1
Ksh93 做了很多工作来避免分叉。我不知道它如何知道如何处理第一种情况,因为 atruss
表明它只调用一次write(2)
带有最终结果的调用。
David 可能扫描了 Macro.c 中的命令并知道他可能在内部处理“echo”。
我能说的是,我去年重写了“Bourne Shell”的解析器和解释器,主要是减少了fork的数量,并用vfork()
调用代替了很多fork。目前,这使得 Bourne Shell 成为仅次于 ksh93 的第二快的 shell。您可能bosh
也想使用它来运行测试。
顺便说一句:ksh93 一般情况下会避免分叉。它实现了一个包含所有先前全局变量的结构,如果使用“全局”变量结构指针的不同实例调用它,则这使得 shell 代码可重入。
只要存在子 shell,ksh93 就会使用此方法(cmd)
。
这次重写的原因是David在他的笔记本电脑上使用Win-DOS,他不喜欢缓慢的Cygwin,所以他编写了UWIN并直接在Win-DOS上使用ksh93。由于Win-DOS上没有fork()
,他需要寻找新的解决方案......