shell 脚本并发调度控制

在 shell 脚本里有时需要同时启动多个命令实现并发,对于少量程序可以采用将命令放到后台方法实现并发,并使用wait命令等待命令执行完毕。

for beam in $Allbeam
do	
	{
		<app> <parameter>
	}&
done
wait

但是当需要执行程序数量较多,并且每个程序对硬件资源需求比较大时,此时需要在脚本内实现对同时执行的程序数量进行控制,即进程调度功能。

实现进程调度最简单的方式是使用管道方法。在linux环境里管道有一个特点,如果管道中没有数据,那么取管道数据的操作就会滞留,直到管道内进入数据,然后读出后才会终止这一操作;同理,写入管道的操作如果没有读取管道的操作,这一动作就会滞留。

使用管道实现并发的一个简单的示例代码如下:

ncpu=10
tmpfile="$$.fifo" # 临时生成管道文件
mkfifo $tmpfile
exec 6<>$tmpfile
rm $tmpfile

for((i=1; i<=$ncpu; i++))
do
    echo $i>&6 # 每次向管道内输出一个字符,也就是一个令牌
done

task=400
for((i=1; i<=$task; i++))
do
	read -u6 cpu
	{
		echo "run task " $task "with cpu " $cpu
		echo $cpu>&6 # 将令牌返回管道中
	}
done

在上述代码中包含三部分。

  1. 首先使用 mkfifo 构造了一个管道文件,并将此管道绑定到标准输出设备6上。此时就可以把实际文件删除,但是标准输出仍然是可用的。
  2. 随后,向管道内输入特定数量的字符,具体数量与并发的进程数相等。
  3. 在实际运行过程中,在并发循环外首先从设备6中读取管道内字符,当字符(令牌)存在时开始执行花括号内的并发指令,并且在执行完毕后会将字符(令牌)重新输入到管道中。

使用管道形式,就实现了并发进程数量始终与管道内输入字符数相同的功能,即在shell脚本内实现进程调度功能。