gpt4 book ai didi

bash - 如何计算 fork (子?)进程的数量

转载 作者:行者123 更新时间:2023-12-04 04:49:02 24 4
gpt4 key购买 nike

其他人已经编写了 (TM) 一些 bash 脚本,该脚本 fork 了很多子进程。它需要优化。但我正在寻找一种方法来衡量问题的“严重程度”。

我可以/如何获得一个计数,说明这个脚本全部/递归地 fork 了多少个子进程?

这是现有 fork 代码的简化版本 - 一个穷人的 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

脚本需要 20 秒来查找 $1在 1000 行输入中。这段代码 fork 了太多子进程。在实际代码中,使用 progA | progB | progC 在每行上运行更长的管道(例如 grep) , cut , awk , sed等等。

这是一个繁忙的系统,有很多其他的事情在进行,因此计算脚本运行时整个系统上有多少进程对我有用,但我更喜欢计算由这个脚本和后代启动的进程。我想我可以分析脚本并自己计算它,但是脚本很长而且相当复杂,所以如果可能的话,我只想用这个计数器来检测它以进行调试。

澄清:
  • 我不是在寻找 $$ 下的进程数在任何给定时间(例如通过 ps ),但在脚本的整个生命周期中运行的进程数。
  • 我也不是在寻找这个特定示例脚本的更快版本(我可以做到)。我正在寻找一种方法来确定首先优化 30 多个脚本中的哪一个以使用 bash 内置程序。
  • 最佳答案

    你可以数fork ed 进程只是捕获 SIGCHLD 信号。如果您可以编辑脚本文件,那么您可以这样做:

    set -o monitor # or set -m
    trap "((++fork))" CHLD

    所以 fork变量将包含 fork 的数量。最后你可以打印这个值:
    echo $fork FORKS

    对于 1000 行的输入文件,它将打印:
    3000 FORKS

    这段代码 fork 有两个原因。一人一份 expr ...一个用于 `echo ...|grep...` .所以在阅读while循环时它 fork s 每次读取一行时;在处理while循环时它 fork s 2 次(一次是因为 expr ...,一次是因为 `echo ...|grep ...`)。因此,对于 1000 行的文件,它会 fork 3000 次。

    但这并不准确!它只是调用 shell 完成的 fork 。 fork 比较多,因为 `echo ...|grep...` fork 开始 运行此代码。但之后它也 fork 了两次:一次为 echo一个用于 grep .所以实际上是 3 fork s,没有之一。所以它是 5000 个 fork ,而不是 3000 个。

    如果您还需要计算 fork ( fork ...)的 fork (或者您无法修改 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 是原始 脚本的 PID)。

    评论

    实际上在这种情况下所有 fork s 可以避免:

    而不是
    cnt=`expr $cnt + 1`

    使用
    ((++cnt))

    而不是
    matches=`echo ${lineArray[$cnt]}|grep $match`
    if [ "$matches" ] ; then
    echo ${lineArray[$cnt]}
    fi

    您可以使用 的内部模式匹配:
    [[ ${lineArray[cnt]} =~ $match ]] && echo ${lineArray[cnt]}

    请注意 =~使用 ERE 而不是 RE(如 grep)。所以它的行为就像 (或 grep -E),而不是 .

    我假设定义的 lineArray不是毫无意义的(否则在读取循环中可以测试匹配并且不需要 lineArray)并且它也用于其他目的。在这种情况下,我可能会建议一个更短的版本:
    readarray -t lineArray <infile 

    for line in "${lineArray[@]}";{ [[ $line} =~ $match ]] && echo $line; }

    第一行读取完整 infilelineArray没有任何循环。第二行是逐个元素处理数组。

    措施

    1000 行的原始脚本(在 上):
    $ 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) 文件。其他情况 , , 过度表现纯粹 解决方案。但这应该通过性能测试来检查。

    上千行 我得到以下信息:
    $ time grep Solaris infile # Solaris is not in the infile

    real 0m0.001s
    user 0m0.000s
    sys 0m0.001s

    关于bash - 如何计算 fork (子?)进程的数量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17718508/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com