0x01 背景

在bash中,我们可以通过&来让程序在后台运行。如果在for循环中,每个循环处理的处理可以并行执行,那么将每个循环设置在后台执行可以大大加快运行速度:

#!/bin/bash
for ip in 10.3.8.{1..255};
do
    (
    ping $ip -c2 &> /dev/null
    if [ $? -eq 0 ];
    then
        echo "$ip up..."
    fi
    )&
done
wait
echo done

但是,当for循环的次数太多,开启大量的后台进程,如果超过了CPU的核心数,仅仅是调度不同的进程运行,进程中断恢复时的上下文切换也会增加很多损耗。因此,我们需要控制同时执行的后台进程数量。

0x02 实现

思路:设置当前可以并行执行的进程数量,通过$! 记录上一个后台执行程序的PID放入数组中,通过ps -p PID检查数组中的PID就可以知道当前哪些进程退出了,之后便可以创建新的后台进程来执行。

#!/bin/bash
# author: thinkycx
# date: 2019-01-14
# Usage:
#     run jobs at the same time with multiple process.
# Permanent-URL: https://gist.github.com/thinkycx/f1350b209733afa71523a9f1ec057c96
parallel(){
    nCPU=3 # 同时执行的进程数量
    PID=()
 
    for((i=0;i<5;i++)){
        # while process number >= nCPU
        echo "============================================"
        # echo "[*] PID() len: " ${#PID[*]}
        echo "[*] PID() content:" ${PID[*]}
 
        # echo "[-] Checking PID() length >= nCPU ?"
        while [ ${#PID[*]} -ge $nCPU ] ;
        do
            for pid in ${PID[*]}; do
                ps -p $pid >/dev/null
                isQuit=$?
                if [ $isQuit -eq 1 ] ; then
                    PID=(${PID[*]/$pid})
                    echo "[*] Checking... $pid stops. PID() len: " ${#PID[*]}
                fi
            done
            sleep 1;
        done
        # echo "[*] After check. " ${PID[*]}
 
        # start new process background
        (
            echo -e "\\t [x] Job $i start to run..."
            sleep 5
            echo -e "\\t [x] Job $i done. "
 
        )&
        PID[$i]=$!               # 向数组中添加上一个运行的后台进程的pid
        sleep 1
        echo -e "\\t [*] process PID is :" ${PID[$i]}
 
    }
}
 
parallel
wait
echo "[*] Congratilations. All jobs done! "

0x03 效果

0x04 参考

  1. Bash脚本实现批量作业并行化