AIX 下没有现成的 timeout
命令来限时运行命令,于是就想着自己实现一个类似的脚本。本来以为挺简单的一件事情,结果埋者一堆坑。
最初的结果如下:
#! /usr/bin/ksh
waitfor=$1
shift
command=$*
$command &
commandpid=$!
(sleep $waitfor;kill $commandpid) & # 坑1
watchdogpid=$!
wait $commandpid
kill $watchdogpid # 坑2
这里有两个需要关注的地方:
(sleep $waitof;kill $commandpid) &
在超时杀掉工作命令后就退出了,工作命令被杀掉之后wait $commandpid
执行完成,主进程继续执行kill $watchdogpid
. 然而由于监控进程早已退出,在忙碌的系统中,可能出现$watchdogpid
被其他进程重复使用,导致误杀其他进程的风险。 要解决这一风险,可以让监控进程在杀掉工作进程后再等待一段时间,以便让主进程杀掉监控进程。kill $watchdogpid
在ksh
中并不会把子进程一起杀掉,也就是说sleep $waitfor
这个进程依然在运行,只不过父进程从$watchdogpid
变成了1
. 不仅如此AIX
上的kill
居然不支持PID
为负数的情况,这使得妄想通过kill -$watchdogpid
杀掉整个进程组变得不可能。
最后经过尝试,发现在ksh交互模式下,用 kill %jobID
的方式是能够将整个 JOB
杀干净的,因此最后的结果如下:
#! /usr/bin/ksh -i
waitfor=$1
shift
command=$*
$command &
commandpid=$!
(sleep $waitfor;kill $commandpid;sleep 1) &
wait $commandpid
kill %2 >/dev/null 2>&1
不过这种实现有个比较大的缺点就是由于整个实现实在交互式ksh环境中执行的,因此会污染 ksh 的 history 命令历史。
教新的 AIX 上搭载的 kill 命令是支持通过将 PID
设置为负值来杀掉整个进程组的,这样一来 timeout
的实现就简单很多了:
timeout()
{
waitfor=$1
shift
command=$*
$command &
commandpid=$!
(sleep $waitfor;kill $commandpid;sleep 1) &
watchdogpid=$!
wait $commandpid
kill -$watchdogpid # 杀掉整个进程组
}