bash编程,后台进程,PID和等待作业退出

时间:2016-11-01 12:00:52

标签: linux multithreading bash shell process

我编写了一个bash脚本来连续运行作业以生成大量的模拟数据。

基本上,一旦脚本运行,它应该不断启动后台进程以生成受限制的数据,该约束不能运行多于32个同时后台作业。这是为了防止进程吞噬所有可用的ram并停止服务器。

我的想法是在后台启动bash功能并存储这些作业的PID。然后在启动32个作业后,使用wait命令wait,直到所有作业PID完成执行。

我认为wait是正确使用的工具,只要在运行wait命令时存在进程的pid(因为模拟需要6个小时才能运行)然后是wait命令将检测到进程退出。

这似乎是一个更好的选择,而不仅仅是轮询进程和检查特定PID的存在,因为PID被循环使用,而另一个进程可能在我们完成相同的PID后启动。 (只是偶然的机会,如果我们运气不好。)

但是,使用wait方法的缺点是,如果进程没有按它们运行的​​顺序退出,那么wait将被调用一个不再存在的PID,除非有新进程 - 使用与我们之前记录的PID相同的PID,此外,如果一个作业比其他作业花费的时间长得多(再次偶然),那么我们将等待一个作业结束,而系统上还有另一个作业31由于我们正在等待最终的PID退出而无法运行的作业...

这可能变得有点难以想象,所以让我添加一些代码......

我使用while循环作为此“算法”的基础

c=0 # count total number of jobs launched (will not really use this here)
PIDS=() # keep any array of PIDs

# maximum number of simultaneous jobs and counter
BATCH_SIZE=32
BATCH_COUNT=0

# just start looping
while true

    # edit: forgot to add this initially
    # just check to see if a job has been run using file existance
    if [ ! -e "$FILE_NAME_1" ]
    then

        # obvious
        if [ "$BATCH_COUNT" -lt "$BATCH_SIZE" ]
        then

            (( BATCH_COUNT += 1 ))

            # this is used elsewhere to keep track of whether a job has been executed (the file existence is a flag)    
            touch "$FILE_NAME_1"
            # call background job, parallel_job_run is a bash function
            parallel_job_run $has_some_arguments_but_not_relevent
            # get PID
            PID=$!
            echo "[ JOB ] : Launched job as PID=$PID"
            PIDS+=($PID)

            # count total number of jobs
            ((c=c+1))
        fi

    else
        # increment file name to use as that file already exists        
        # the "files" are for input/output
        # the details are not particularly important
    fi

    true # prevent exit

# the following is a problem 
do      
    if (( BATCH_COUNT < BATCH_SIZE ))
    then
        continue
    else
        # collect launched jobs
        # this does not collect jobs in the order that they finish
        # it will first wait for the first PID in the array to exit
        # however this job may be the last to finish, in which case
        # wait will be called with other array values with PID's which
        # have already exited, and hence it is undefined behaviour
        # as to whether we wait for a PID which doesn't exist (no problem)
        # or a new process may have started which re-uses our PID
        # and therefore we are waiting for someone else's process
        # to finish which is nothing to do with our own jobs!
        # we could be waiting for the PID of someone else's tty login
        # for example!
        for pid in "${PIDS[@]}"
        do
            wait $pid || echo "failed job PID=$pid"
            (( BATCH_COUNT -= 1 ))
        done
    fi

done 

希望代码中的注释和上面的代码和注释的组合应该清楚我想要做的事情。

我的另一个想法是用另一个循环替换最后的for循环,该循环不断检查每个PID是否存在。 (轮询。)这可以与sleep 1结合使用以防止CPU占用。然而,问题就像以前一样,我们的进程可能会退出释放它的PID,并且可能碰巧运行另一个进程来获取该PID。这种方法的优点是,在前一个进程退出新进程之前,我们永远不会等待大约1秒钟。

有人可以就如何处理我在这里遇到的问题向我提出任何建议吗?

我今天会不断更新这个问题 - 例如,如果我找到任何新信息,并通过格式化/重写部分来使其更清晰。

1 个答案:

答案 0 :(得分:1)

如果对-n使用wait选项,它将等待下一个进程完成,无论其PID如何。所以,这可能是一个解决方案。

此外,Linux并不像您暗示的那样立即回收PID。它按顺序为新进程分配下一个可用的PID,并且只有在它耗尽了最大可用PID后才从头开始。