有人编写了一些bash脚本(TM),该脚本分叉了很多子进程。它需要优化。但我正在寻找一种方法来衡量问题的“严重程度”。
我/如何获得一个计数,说明该脚本全部/递归分叉了多少个子流程?
这是现有分叉代码的简化版本-一个穷人的grep:
#!/bin/bash
file=/tmp/1000lines.txt
match=$1
let cnt=0
while read line
do
cnt=`expr $cnt + 1`
lineArray[$cnt]="${line}"
done < $file
totalLines=$cnt
cnt=0
while [ $cnt -lt $totalLines ]
do
cnt=`expr $cnt + 1`
matches=`echo ${lineArray[$cnt]}|grep $match`
if [ "$matches" ] ; then
echo ${lineArray[$cnt]}
fi
done
$1
在1000行输入中查找脚本需要20秒。这段代码分叉了太多的子流程。在实际代码,有较长的配管(例如progA | progB | progC
)使用每行操作grep
,cut
,awk
,sed
等。
这是一个忙碌的系统,正在运行许多其他内容,因此,对脚本运行时整个系统上有多少个进程进行了分叉的计数对我来说很有用,但我宁愿选择一个此脚本及其后代启动的进程。而且我想我可以自己分析脚本并对其进行计数,但是该脚本又长又相当复杂,因此,如果可能的话,我想使用此计数器对其进行调试。
澄清:
$$
在任何给定时间(例如,通过ps
)寻找进程数,而是在脚本的整个生命周期内运行的进程数。您可以fork
简单地捕获SIGCHLD信号来计算ed过程。如果您可以编辑脚本文件,则可以执行以下操作:
set -o monitor # or set -m
trap "((++fork))" CHLD
因此fork
变量将包含分叉数。最后,您可以打印以下值:
echo $fork FORKS
对于1000行输入文件,它将打印:
3000 FORKS
此代码分叉有两个原因。一个一个expr ...
,一个一个`echo ...|grep...`
。因此,在while循环读取中,它fork
是每次读取一行时所用的时间。在处理while循环中,它fork
是2次(一次是因为,expr ...
一次是`echo ...|grep ...`
)。因此,对于1000行文件,它会分叉3000次。
但是,这不完全正确!它只是调用外壳程序完成的分叉。有更多的派生,因为`echo ...|grep...`
派生启动bash来运行此代码。但是之后也有两次分叉:一次为echo
,一次为grep
。因此实际上是3fork
秒,而不是1秒。所以它是5000福克斯,而不是3000。
如果您还需要计算叉子(叉子...)的叉子(或者您无法修改bash脚本,或者您希望它从其他脚本中执行),则可以使用更精确的解决方案
strace -fo s.log ./x.sh
它将打印如下行:
30934 execve("./x.sh", ["./x.sh"], [/* 61 vars */]) = 0
然后,您需要使用类似这样的数字来计数唯一的PID(第一个数字是PID):
awk '{n[$1]}END{print length(n)}' s.log
如果使用此脚本,我会得到5001
(+1是原始bash脚本的PID )。
评论
实际上,在这种情况下,fork
可以避免所有:
代替
cnt=`expr $cnt + 1`
使用
((++cnt))
代替
matches=`echo ${lineArray[$cnt]}|grep $match`
if [ "$matches" ] ; then
echo ${lineArray[$cnt]}
fi
您可以使用bash的内部模式匹配:
[[ ${lineArray[cnt]} =~ $match ]] && echo ${lineArray[cnt]}
注意bash =~
使用ERE而不是RE(例如grep)。因此它的行为类似于egrep(或grep -E
),而不是grep。
我认为定义lineArray
不是没有意义的(否则在读取循环中可以测试匹配并且lineArray
不需要),并且它也可以用于其他目的。在这种情况下,我可能会建议使用较短的版本:
readarray -t lineArray <infile
for line in "${lineArray[@]}";{ [[ $line} =~ $match ]] && echo $line; }
第一行读取完整的内容infile
,lineArray
没有任何循环。第二行是逐元素处理数组。
措施
1000行的原始脚本(在cygwin上):
$ time ./test.sh
3000 FORKS
real 0m48.725s
user 0m14.107s
sys 0m30.659s
修改版
FORKS
real 0m0.075s
user 0m0.031s
sys 0m0.031s
3000 FORKS
real 0m4.745s
user 0m1.015s
sys 0m4.396s
和
FORKS
real 0m0.028s
user 0m0.022s
sys 0m0.005s
因此,此版本完全不使用fork
(或clone
)。我可能建议仅将此版本用于较小的文件(<100 KiB)。在其他情况下,grap,egrep,awk over执行纯bash解决方案。但这应该通过性能测试进行检查。
对于Linux上的一千行,我得到了以下内容:
$ time grep Solaris infile # Solaris is not in the infile
real 0m0.001s
user 0m0.000s
sys 0m0.001s
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句