bash脚本在bash中运行并行子进程时的奇怪行为



下面的脚本用于在bash中运行并行子进程,这与在bash中并行运行有限数量的子进程略有不同。

#!/bin/bash
set -o monitor # means: run background processes in a separate processes...
N=1000
todo_array=($(seq 0 $((N-1))))
max_jobs=5
trap add_next_job CHLD
index=0
function add_next_job {
    if [[ $index -lt ${#todo_array[@]} ]]
    then
    do_job $index &
    index=$(($index+1))
    fi
}
function do_job {
    echo $1 start
    time=$(echo "scale=0;x=$RANDOM % 10;scale=5;x/20+0.05" |bc);sleep $time;echo $time
    echo $1 done
}
while [[ $index -lt $max_jobs ]] && [[ $index -lt ${#todo_array[@]} ]]
do
    add_next_job
done
wait

作业是在0.05:0.05:5.00中随机选择一个数字,然后睡那么多秒。

例如,当N=10时,样本输出为

1 start
4 start
3 start
2 start
0 start
.25000
2 done
5 start
.30000
3 done
6 start
.35000
0 done
7 start
.40000
1 done
8 start
.40000
4 done
9 start
.05000
7 done
.20000
5 done
.25000
9 done
.45000
6 done
.50000
8 done

,共30行。

但是对于像1000这样的大N,结果可能很奇怪。一次运行产生2996行输出,其中998行表示start,999行表示done,999行表示float number。644和652在start中缺失,644在done中缺失。

这些测试是在Arch Linux上运行的bash 4.2.10(2)。在debian stable上使用bash 4.1.5(1)也可以产生类似的结果。

编辑:我在moreutils和GNU并行中尝试了并行测试。moreutils中的Parallel也有同样的问题。但是GNU parallel工作得很好

我认为这只是由于所有的子进程继承了相同的文件描述符,并试图并行追加到它。很少有两个进程竞争,并且都在同一位置开始追加,其中一个覆盖另一个。这实际上与其中一条注释所建议的相反。

你可以很容易地通过管道重定向来检查这一点,比如your_script | tee file,因为管道有关于单个write()调用传递的数据原子性的规则,这些调用小于特定大小。

还有另一个问题,这是类似的(我认为它只是涉及两个线程都快速写数字),这也解释了,但我找不到它。

我唯一能想到的就是你的资源快用完了;检查"ulimit -a"并查找"max user processes"。如果这少于你想要生成的进程数,你将以错误告终。

尝试为您的用户(如果您不是作为根用户运行)设置更高的限制。在Redhatish系统上,您可以这样做:

将这行添加到/etc/pam.d/login:

session required pam_limits.so

在/etc/security/limits.conf中添加以下内容:

myuser soft nproc 1000
myuser hard nproc 1024

,其中"myuser"是被授予权限的用户名,1000是默认值"max userprocesses",1024是最大用户进程数。软限制和硬限制不能分开太多。它只说明用户可以在shell中使用"ulimit"命令设置自己的权限。
因此,myuser开始时总共有1000个进程(包括shell和所有其他派生进程),但可以使用ulimit:

将其提高到1024。
$ ulimit -u
1000
$ ulimit -u 1024
$ ulimit -u
1024
$ ulimit -u 2000
-bash: ulimit: max user processes: cannot modify limit: Operation not permitted

不需要重新启动,它立即工作。

祝你好运!亚历克斯。

最新更新