shell如何在后台执行
1.nohup命令
通常我们都是远程登录linux终端,而当我们退出终端时在之前终端运行的程序都会终止,有时候先想要退出终端也要程序继续执行这时nohup就登场了。nohup命令可以将程序以忽略挂起信号的方式运行起来,被运行的程序的输出信息将不会显示到终端。
nohup command > myout.file 2>&1 &
2.&后台执行
在命令后面加 & 可以让程序在后台执行
command &
3.Ctrl + z
当一个程序正在执行并且占用当前终端时我们同时按下 Ctrl + z ,这样就会把正在执行的前台程序放到后台挂起。
并发执行
1.正常执行
#!/bin/bash Njob=10 #任务总数 for ((i=0; i<$Njob; i++)); do { echo "progress $i is sleeping for 1 seconds zzz…" sleep 1 } done echo -e "time-consuming: $SECONDS seconds" #显示脚本执行耗时
执行结果
progress 0 is sleeping for 1 seconds zzz… progress 1 is sleeping for 1 seconds zzz… progress 2 is sleeping for 1 seconds zzz… progress 3 is sleeping for 1 seconds zzz… progress 4 is sleeping for 1 seconds zzz… progress 5 is sleeping for 1 seconds zzz… progress 6 is sleeping for 1 seconds zzz… progress 7 is sleeping for 1 seconds zzz… progress 8 is sleeping for 1 seconds zzz… progress 9 is sleeping for 1 seconds zzz… -e time-consuming: 10 seconds
2.并发后台执行
#!/bin/bash Njob=10 for ((i=0; i<$Njob; i++)); do echo "progress $i is sleeping for 3 seconds zzz…" sleep 3 & #循环内容放到后台执行 done wait #等待循环结束再执行wait后面的内容 echo -e "time-consuming: $SECONDS seconds" #显示脚本执行耗时
执行结果
progress 0 is sleeping for 3 seconds zzz… progress 1 is sleeping for 3 seconds zzz… progress 2 is sleeping for 3 seconds zzz… progress 3 is sleeping for 3 seconds zzz… progress 4 is sleeping for 3 seconds zzz… progress 5 is sleeping for 3 seconds zzz… progress 6 is sleeping for 3 seconds zzz… progress 7 is sleeping for 3 seconds zzz… progress 8 is sleeping for 3 seconds zzz… progress 9 is sleeping for 3 seconds zzz… -e time-consuming: 3 seconds
这种方式从功能上实现了使用shell脚本并行执行多个循环进程,但是它缺乏控制机制。
for设置了Njob次循环,同一时间Linux就触发Njob个进程一起执行。假设for里面执行的是scp,在没有pam_limits和cgroup限制的情况下,很有可能同一时刻过多的scp任务会耗尽系统的磁盘IO、连接数、带宽等资源,导致正常的业务受到影响。
一个应对办法是在for循环里面再嵌套一层循环,这样同一时间,系统最多只会执行内嵌循环限制值的个数的进程。不过还有一个问题,for后面的wait命令以循环中最慢的进程结束为结束(水桶效应)。如果嵌套循环中有某一个进程执行过程较慢,那么整体这一轮内嵌循环的执行时间就等于这个“慢”进程的执行时间,整体下来脚本的执行效率还是受到影响的。
分批并行的方式并发执行
#!/bin/bash NQ=3 num=5 for ((i=0; i<$NQ; i++)); do for ((j=0; j<$num; j++)); do echo "progress $i is sleeping for 3 seconds zzz…" sleep 3 & done wait done #等待循环结束再执行wait后面的内容 echo -e "time-consuming: $SECONDS seconds" #显示脚本执行耗时
执行结果
progress 0 is sleeping for 3 seconds zzz… progress 0 is sleeping for 3 seconds zzz… progress 0 is sleeping for 3 seconds zzz… progress 0 is sleeping for 3 seconds zzz… progress 0 is sleeping for 3 seconds zzz… progress 1 is sleeping for 3 seconds zzz… progress 1 is sleeping for 3 seconds zzz… progress 1 is sleeping for 3 seconds zzz… progress 1 is sleeping for 3 seconds zzz… progress 1 is sleeping for 3 seconds zzz… progress 2 is sleeping for 3 seconds zzz… progress 2 is sleeping for 3 seconds zzz… progress 2 is sleeping for 3 seconds zzz… progress 2 is sleeping for 3 seconds zzz… progress 2 is sleeping for 3 seconds zzz… -e time-consuming: 9 seconds
3.使用模拟队列来控制进程数量
要控制后台同一时刻的进程数量,需要在原有循环的基础上增加管理机制。
一个方法是以for循环的子进程PID做为队列元素,模拟一个限定最大进程数的队列(只是一个长度固定的数组,并不是真实的队列)。队列的初始长度为0,循环每创建一个进程,就让队列长度+1。当队列长度到达设置的并发进程限制数之后,每隔一段时间检查队列,如果队列长度还是等于限制值,那么不做操作,继续轮询;如果检测到有并发进程执行结束了,那么队列长度-1,轮询检测到队列长度小于限制值后,会启动下一个待执行的进程,直至所有等待执行的并发进程全部执行完。
#!/bin/bash Njob=15 #任务总数 Nproc=5 #最大并发进程数 function PushQue { #将PID值追加到队列中 Que="$Que $1" Nrun=$(($Nrun+1)) } function GenQue { #更新队列信息,先清空队列信息,然后检索生成新的队列信息 OldQue=$Que Que=""; Nrun=0 for PID in $OldQue; do if [[ -d /proc/$PID ]]; then PushQue $PID fi done } function ChkQue { #检查队列信息,如果有已经结束了的进程的PID,那么更新队列信息 OldQue=$Que for PID in $OldQue; do if [[ ! -d /proc/$PID ]]; then GenQue; break fi done } for ((i=1; i<=$Njob; i++)); do echo "progress $i is sleeping for 3 seconds zzz…" sleep 3 & PID=$! PushQue $PID while [[ $Nrun -ge $Nproc ]]; do # 如果Nrun大于Nproc,就一直ChkQue ChkQue sleep 0.1 done done wait echo -e "time-consuming: $SECONDS seconds" #显示脚本执行耗时
执行结果
progress 1 is sleeping for 3 seconds zzz… progress 2 is sleeping for 3 seconds zzz… progress 3 is sleeping for 3 seconds zzz… progress 4 is sleeping for 3 seconds zzz… progress 5 is sleeping for 3 seconds zzz… progress 6 is sleeping for 3 seconds zzz… progress 7 is sleeping for 3 seconds zzz… progress 8 is sleeping for 3 seconds zzz… progress 9 is sleeping for 3 seconds zzz… progress 10 is sleeping for 3 seconds zzz… progress 11 is sleeping for 3 seconds zzz… progress 12 is sleeping for 3 seconds zzz… progress 13 is sleeping for 3 seconds zzz… progress 14 is sleeping for 3 seconds zzz… progress 15 is sleeping for 3 seconds zzz… -e time-consuming: 3 seconds
这种使用队列模型管理进程的方式在控制了后台进程数量的情况下,还能避免个别“慢”进程影响整体耗时的问题:
4.使用fifo管道特性来控制进程数量
管道是内核中的一个单向的数据通道,同时也是一个数据队列。具有一个读取端与一个写入端,每一端对应着一个文件描述符。
命名管道即FIFO文件,通过命名管道可以在不相关的进程之间交换数据。FIFO有路径名与之相关联,以一种特殊设备文件形式存在于文件系统中。
FIFO有两种用途:
• FIFO由shell使用以便数据从一条管道线传输到另一条,为此无需创建临时文件,常见的操作cat file|grep keyword就是这种使用方式;
• FIFO用于客户进程-服务器进程程序中,已在客户进程与服务器进程之间传送数据,下面的例子将使用这种方式。
根据FIFO文件的读规则(参考http://www.cnblogs.com/yxmx/articles/1599187.html),如果有进程写打开FIFO,且当前FIFO内没有数据,对于设置了阻塞标志的读操作来说,将一直阻塞状态。
利用这一特性可以实现一个令牌机制。设置一个行数等于限定最大进程数Nproc的fifo文件,在for循环中设置创建一个进程时先read一次fifo文件,进程结束时再write一次fifo文件。如果当前子进程数达到限定最大进程数Nproc,则fifo文件为空,后续执行的并发进程被读fifo命令阻塞,循环内容被没有触发,直至有某一个并发进程执行结果并做写操作(相当于将令牌还给池子)。
需要注意的是,当并发数较大时,多个并发进程即使在使用sleep相同秒数模拟时,也会存在进程调度的顺序问题,因而并不是按启动顺序结束的,可能会后启动的进程先结束。
#!/bin/bash Njob=15 #任务总数 Nproc=5 #最大并发进程数 mkfifo ./fifo.$$ && exec 9<> ./fifo.$$ #通过文件描述符777访问fifo文件 for ((i=0; i<$Nproc; i++)); do #向fifo文件先填充等于Nproc值的行数 echo "init time add $i" >&9 done for ((i=0; i<$Njob; i++)); do { read -u 9 #从fifo文件读一行 echo "progress $i is sleeping for 3 seconds zzz…" sleep 3 echo "real time add $(($i+$Nproc))" 1>&9 #sleep完成后,向fifo文件重新写入一行 } & done wait echo -e "time-consuming: $SECONDS seconds" rm -f ./fifo.$$
执行结果
progress 0 is sleeping for 3 seconds zzz… progress 1 is sleeping for 3 seconds zzz… progress 2 is sleeping for 3 seconds zzz… progress 3 is sleeping for 3 seconds zzz… progress 4 is sleeping for 3 seconds zzz… progress 5 is sleeping for 3 seconds zzz… progress 6 is sleeping for 3 seconds zzz… progress 8 is sleeping for 3 seconds zzz… progress 12 is sleeping for 3 seconds zzz… progress 13 is sleeping for 3 seconds zzz… progress 9 is sleeping for 3 seconds zzz… progress 11 is sleeping for 3 seconds zzz… progress 14 is sleeping for 3 seconds zzz… progress 10 is sleeping for 3 seconds zzz… progress 7 is sleeping for 3 seconds zzz… -e time-consuming: 10 seconds
原文地址:
Shell脚本实现并发多进程
Shell脚本并发执行