为什么 bash 4.3 不断在脚本中分配内存

为什么 bash 4.3 不断在脚本中分配内存

为什么这个shell脚本在bash中逐渐消耗更多的内存?我没有使用任何局部变量。尽管 的值cpid在每次迭代时都会发生变化,因为ls会立即退出,但它永远不应该变得太大。

相关的 shell 脚本只是监视子进程并检查它是否启动。该脚本名为run-always

set -euf

# monitor a child process
# see if it's up

cpid=
while : ; do
    # if cpid isn't set, spawn child
    if [ -z "$cpid" ]; then
       "$@" &
       cpid="$!"
    fi

    # if child isn't active unset cpid
    if ps -o pid= -p "$cpid"; then
        :
    else
        cpid=
    fi

    sleep 1
done

如果我将它与立即退出的进程一起使用(例如ls),那么当我执行脚本时,我们会得到以下内存使用情况dash run-always ls

4476kb 并且永远不会增长。

这是我用来在 Linux 上每秒获取一次内存使用情况的命令……可能有一种方法可以直接从 获取内存和峰值内存使用情况ps,但这可以完成任务。

 while : ; do cat /proc/"$(ps a | grep run-always | grep -v grep | awk '{print $1}')"/status | grep -i 'vmsize\|vmpeak' ; sleep 1; done

当我用 执行上面的脚本时dash,内存使用量永远不会增加,它在我的机器上稳定地徘徊在 4476kb。

然而,当我使用 bash ( bash run-always ls) 时,每隔十秒左右它就会分配另外几千字节的内存并且永远不会释放它。

bash 为什么要这样做?

输出示例:

VmPeak:    16676 kB
VmSize:    16676 kB
VmPeak:    16676 kB
VmSize:    16676 kB
VmPeak:    16676 kB
VmSize:    16676 kB
VmPeak:    16676 kB
VmSize:    16676 kB
VmPeak:    16676 kB
VmSize:    16676 kB
VmPeak:    16676 kB
VmSize:    16676 kB
VmPeak:    16676 kB
VmSize:    16676 kB
VmPeak:    16676 kB
VmSize:    16676 kB
VmPeak:    16676 kB
VmSize:    16676 kB
VmPeak:    16676 kB
VmSize:    16676 kB
VmPeak:    16676 kB
VmSize:    16676 kB
VmPeak:    16680 kB
VmSize:    16680 kB
VmPeak:    16680 kB
VmSize:    16680 kB
VmPeak:    16680 kB
VmSize:    16680 kB
VmPeak:    16680 kB
VmSize:    16680 kB
VmPeak:    16680 kB
VmSize:    16680 kB
VmPeak:    16680 kB
VmSize:    16680 kB
VmPeak:    16680 kB
VmSize:    16680 kB
VmPeak:    16680 kB
VmSize:    16680 kB
VmPeak:    16680 kB
VmSize:    16680 kB
VmPeak:    16684 kB
VmSize:    16684 kB
VmPeak:    16684 kB
VmSize:    16684 kB
VmPeak:    16684 kB
VmSize:    16684 kB
VmPeak:    16684 kB
VmSize:    16684 kB
VmPeak:    16684 kB
VmSize:    16684 kB
VmPeak:    16684 kB
VmSize:    16684 kB
VmPeak:    16684 kB
VmSize:    16684 kB
VmPeak:    16684 kB
VmSize:    16684 kB
VmPeak:    16684 kB
VmSize:    16684 kB
VmPeak:    16684 kB
VmSize:    16684 kB
VmPeak:    16684 kB
VmSize:    16684 kB
VmPeak:    16684 kB
VmSize:    16684 kB
VmPeak:    16684 kB
VmSize:    16684 kB
VmPeak:    16684 kB
VmSize:    16684 kB
VmPeak:    16688 kB
VmSize:    16688 kB
VmPeak:    16688 kB
VmSize:    16688 kB
VmPeak:    16688 kB
VmSize:    16688 kB
VmPeak:    16688 kB
VmSize:    16688 kB
VmPeak:    16688 kB
VmSize:    16688 kB
VmPeak:    16688 kB
VmSize:    16688 kB
VmPeak:    16688 kB
VmSize:    16688 kB
VmPeak:    16688 kB
VmSize:    16688 kB
VmPeak:    16688 kB
VmSize:    16688 kB
VmPeak:    16688 kB
VmSize:    16688 kB
VmPeak:    16692 kB
VmSize:    16692 kB

答案1

Bash 将每个后台进程保存在活动作业表中。由于您在没有显式检查旧作业的情况下生成新作业,因此该表可能会无限制地增加。如果您在后台处理该进程或在启动下一个后台进程之前disown检查退出状态,则问题就会消失。jobs

例如,此版本的脚本不会增加内存使用量:

set -euf

# monitor a child process
# see if it's up

cpid=
while : ; do
    # if cpid isn't set, spawn child
    if [ -z "$cpid" ]; then
       "$@" &
       disown $!       # Don't waste memory on this red-headed child
       cpid="$!"
    fi

    # if child isn't active unset cpid
    if ps -o pid= -p "$cpid"; then
        :
    else
        cpid=
    fi

    sleep 1
done

答案2

由于您已经发现这个问题不会出现在 dash 中,所以很明显这很可能是由 bug 引起的。

如果您使用 bash-3.x,它也会增长,但速度非常慢 - 仍然比其他 shell 快。

我建议您针对您的 bash 版本制作错误报告。

顺便说一句:我做了一些测试,只有两个 shell 根本没有增长 - 无论您等待多长时间:以及尚未转换为使用 malloc() 而不是 sbrk() 的mksh原始 shell 。Bourne Shell

所有其他贝壳生长都非常缓慢。

相关内容