如果进程的运行时间超过某个预定值,则自动终止进程

如果进程的运行时间超过某个预定值,则自动终止进程

我想从 shell 脚本启动一个进程,允许它与用户交互(所以我不能只是将它置于后台,获取 PID,启动,sleep然后进行健全性检查等kill),但如果它在一段时间后仍在运行(比如说,两分钟),只有那时我才想杀死它。

目的是简单地为没有内置该功能的程序实现挂钟运行时超时。

有没有办法做到这一点,最好是通过 bash shell 脚本?

答案1

在一个足够新的 Linux 系统上(更准确地说,GNU coreutils ≥8.5),它很简单

timeout 2m myprogram

仅使用 POSIX shell 功能,您可以接近,但如果发生超时,则会出现一个小的竞争条件(理论上,第二个后台进程的 PID 可以在kill $s执行之前重用)。

myprogram <>/dev/tty & p=$!;
{ sleep 120; kill $p; } & s=$!;
wait $p; kill $s

即使您需要程序的输出,有一种很好的方法也可以工作,并且没有竞争条件。这是一个巧妙的技巧,利用向进程组。这是由于斯蒂芬·希门尼斯

sh -ic '{ myprogram <>/dev/tty; kill 0; } |
        { sleep 120; kill 0; }'

答案2

BASH 常见问题解答条目 #68:“如何运行命令,并让它在 N 秒后中止(超时)?”

首先检查您正在运行的命令是否可以直接被告知超时。这里描述的方法是“hacky”变通方法,用于强制命令在经过一定时间后终止。正确配置命令始终优于以下替代方案。

如果该命令没有对在指定时间后停止的本机支持,那么最好的替代方案是一些称为 timeout 和 doalarm 的外部命令。一些 Linux 发行版以软件包的形式提供 tct 版本的超时。还有一个 GNU 版本的超时,包含在最近的 coreutils 版本中。

注意:默认情况下,某些超时实现会发出 SIGKILL (kill -9),这与拔掉电源线大致相同(使程序没有机会提交其工作,通常会导致其数据损坏)。您应该使用一个允许程序自行关闭的信号(SIGTERM)。有关 SIGKILL 的更多信息,请参阅进程管理。

doalarm 和 timeout 之间的主要区别在于 doalarm 在设置闹钟后“执行”程序,这使得它在 WrapperScript 中变得非常精彩;而 timeout 将程序作为子进程启动,然后挂起(两个进程同时存在),这使它有机会在必要时发送多个信号。

如果您没有或不想要上述程序之一,您可以使用 perl one-liner 设置 ALRM,然后在时间限制内执行您想要运行的程序。无论如何,您必须了解您的程序使用 SIGALRM 做什么;定期更新的程序通常使用 ALRM 来实现此目的,并在收到该信号时更新而不是死掉。

doalarm() { perl -e 'alarm shift; exec @ARGV' "$@"; }

doalarm ${NUMBER_OF_SECONDS_BEFORE_ALRMING} program arg arg ...

如果你不能或不会使用这些程序之一(这些程序实际上应该在 30 年前就包含在基本的 Unix 核心实用程序中!),那么你能做的最好的就是进行如下丑陋的 hack:

 command & pid=$!
 { sleep 10; kill $pid; } &

您很快就会发现,如果它在交互式 shell 中运行,无论超时条件是否启动,都会产生相当大的混乱。清理它不值得我花时间。此外,它不能与任何需要前台终端的命令(例如 top)一起使用。

可以做类似的事情,但要将命令保持在前台:

 bash -c '(sleep 10; kill $$) & exec command'

kill $$会终止 shell,但 exec 会导致命令接管 shell 的 PID。有必要使用 bash -c 以便调用 shell 不会被替换;在 bash 4 中,可以使用子 shell 来代替:

 ( cmdpid=$BASHPID; (sleep 10; kill $cmdpid) & exec command )

shell 脚本“timeout”(不要与命令“timeout”混淆)使用上面的第二种方法。它的优点是立即工作(不需要编译程序),但存在一些问题,例如程序读取标准输入。

只需使用 timeout 或 doalarm 即可。真的。

答案3

想在吉尔斯给出的解决方案中添加一些内容“所以-停止邪恶”。我把它保存在我的 bashscript 过滤器中。如果我期待一些东西并且如果它超时而没有任何日志,看起来很糟糕,所以我想对其进行一些增强,如下所示:

timeout 1s sleep 3 || echo "sleep exceeded time"

仅当超时超过 1 秒时才会给出日志,否则什么也不给出。

相关内容