我有一些涉及嵌入式系统更新的程序,它们需要按顺序运行,并且需要向单独的系统报告进度.
这些阶段是:
>验证图像;
>将映像文件解压缩为活动磁盘上的文件;和
>将映像安装到备用磁盘.
之后,计算机将重新启动,备用磁盘将变为活动磁盘.
目前,我们报告的初始值为0%,验证结束时为5%,拆包结束时为45%,安装结束时为90%.重新启动后,当新映像开始运行时,将报告100%标记.
现在,尽管我对这种方法感到非常满意,但客户端希望看到更细粒度的更新状态报告.我们已经毫不含糊地让他们知道,此类报告大部分都是人为的,但他们认为有必要让客户满意.
因此,我正在寻找一种方法,可以根据粗糙的时间来报告进度,同时仍然要执行长时间运行的任务并完成工作.
我们知道,从开始到重新启动,整个安装过程大约需要两分钟,而在每个阶段花费的时间将按照给定的百分比分配.
解决方法:
在bash中执行此操作的一种方法是运行一个单独的过程来执行进度报告,并为其提供足够的信息以成功报告.这将涉及:
>起始百分比;
>期末百分比;
>要添加到每个周期的百分比的增量;和
>循环时间.
前两个很明显,它们提供了报告结果的上下限.
另外两个允许您提供报告比率.例如,每秒增加6%将使用这些值(增量= 6,周期= 1),并且每秒将增加6%.
对于较慢的步骤,例如每秒提供0.25%,您可以提供(增量= 1,周期= 4),每四秒钟将添加1%.您实际上并没有报告亚秒级的进度,只是随着时间的推移会平均得出该进度.
那么,这是怎么做的呢?首先,让我们提供一个简单的功能来报告进度.在您的情况下,应将其替换为用于与您提到的“独立系统”进行通信的任何内容.以下代码显示了如何执行此操作:
#!/usr/bin/env bash
doProgress() {
# Simply echo progress using whatever was passed. Date is included
# for debugging purposes.
echo "$(date +%H:%M:%S): $1%"
}
解决方案的“实质”是backgroundReporter()函数,该函数将根据前面提到的四个参数调用doProgress():
backgroundReporter() {
# Get the four parameters.
currVal=$1 ; maxVal=$2 ; deltaVal=$3 ; timeGap=$4
# If signalled to stop, output maximum percentage then exit.
# Note no output if you've already reached the maximum.
trap "[[ \${maxVal} -ne \${lastVal} ]] && doProg \${maxVal} ; exit" HUP
# Infinite loop until signalled.
while : ; do
# Wait for the duration, save current percentage, and
# calculate new percntage (capped at maximum).
sleep ${timeGap}
lastVal=${currVal}
(( currVal = (currVal + deltaVal > maxVal) ? maxVal : currVal + deltaVal ))
# Log only if it's changed.
[[ ${currVal} -ne ${lastVal} ]] && doProg ${currVal}
done
}
唯一棘手的是信号处理.在阶段完成后,将此功能作为后台任务运行的调用方负责向其发送信号.
当然,还有一些基本的测试用例可以证明这一点.首先,我们需要确保退出时杀死运行backgroundReporter()的子进程.当它不运行时执行以下命令:(一个简单的no-op)并杀死-HUP< pid>.运行时:
killCmd=":" ; trap "${killCmd}" EXIT
然后,我们报告初始进度为零,并开始三个阶段.它们比此处指定的问题短,因为这仅用于演示目的:
doProg 0
# 0 -> 10% at 4%/sec, phase takes six seconds.
bgReport 0 10 4 1 & killCmd="kill -HUP $!" ; sleep 6 ; ${killCmd} ; killCmd=":" ; wait
# 10 -> 20% at 1%/2secs, phase takes six seconds.
bgReport 10 20 1 2 & killCmd="kill -HUP $!" ; sleep 6 ; ${killCmd} ; killCmd=":" ; wait
# 20 -> 100% at 30%/sec, phase takes five seconds.
bgReport 20 100 30 1 & killCmd="kill -HUP $!" ; sleep 5 ; ${killCmd} ; killCmd=":" ; wait
而且,从样本运行中您可以看到,这三个阶段的工作非常符合您的期望(右边的评论是我的):
13:15:29: 0%
13:15:30: 4% 4% per sec
13:15:31: 8%
13:15:32: 10% capped at 10% for remainder of 6-sec slot
13:15:37: 11% goes up 1% per 2 secs
13:15:39: 12%
13:15:41: 20% phase finishes, jumps immediately to max
13:15:42: 50% goes up 30% per second
13:15:43: 80%
13:15:44: 100% but capped at 100%
对于给定的实际时间,两分钟,其中阶段是5%的最终验证,45%的最终拆包和90%的最终安装,以下比较合适(阶段稍作修改,使比例更容易实现):
doProg 0
# Validation 0 -> 5%, 5 steps in 6 seconds (~ 1%/1s).
bgReport 0 5 1 1 & killCmd="kill -HUP $!"
validate
${killCmd} ; killCmd=":" ; wait
# Unpacking 5 -> 45%, 40 steps in 50 seconds (4%/5s).
bgReport 5 45 4 5 & killCmd="kill -HUP $!"
unpack
${killCmd} ; killCmd=":" ; wait
# Installing 45 -> 95%, 50 steps in 64 seconds (~ 5%/6s)
bgReport 45 95 5 6 & killCmd="kill -HUP $!"
install
${killCmd} ; killCmd=":" ; wait
有了这些比率,您可以保证每(最多)六秒钟获得一次进度更新,这与原始方法不同,原始方法是将其停留在45%的状态持续一分钟,然后立即跳至95%.