【问题标题】:Limiting the time a program runs in Linux限制程序在 Linux 中运行的时间
【发布时间】:2010-03-05 14:41:17
【问题描述】:

在 Linux 中,我想运行一个程序,但只能运行有限的时间,比如 1 秒。如果程序超过此运行时间,我想终止该进程并显示错误消息。

【问题讨论】:

    标签: linux


    【解决方案1】:

    嗯嗯。 timeout(1).

    描述
    启动 COMMAND,如果在 DURATION 后仍在运行,则将其终止。

    【讨论】:

    • 请评论否决。
    • 我认为这里有一些误解。一个简单的“人工超时”可以表明这个答案是手头问题的最准确答案(虽然有点简洁)。
    • timeout(1) 在 GNU coreutils 中。
    【解决方案2】:

    StackOverflow 不允许我删除我的答案,因为它已被接受。它获得了反对票,因为它位于列表的顶部,下面有一个更好的解决方案。如果您使用的是 GNU 系统,请按照@wRAR 的建议使用timeout。因此,希望您能停止投票,以下是它的工作原理:

    timeout 1s ./myProgram 
    

    您可以使用smhd 表示秒(如果省略,则为默认值)、分钟、小时或天。这里的一个很好的功能是,您可以指定另一个选项 -k 30s(在上面的 1s 之前)以便在再过 30 秒后使用 SIGKILL 杀死它,如果它不响应原始 SIGTERM。

    一个非常有用的工具。现在向下滚动并向上投票@wRAR 的答案。


    对于后人,这是我最初的 - 劣质 - 建议,如果对某人有用,它可能仍然是。

    一个简单的 bash 脚本应该能够为您做到这一点

    ./myProgram &
    sleep 1
    kill $! 2>/dev/null && echo "myProgram didn't finish"
    

    应该这样做。

    $! 扩展为最后一个后台进程(通过使用&),如果它没有杀死任何进程,kill 返回 false,因此只有在实际杀死某些东西时才会执行 echo。

    2>/dev/null 重定向 kill 的 stderr,否则它会打印一些信息告诉你它无法终止进程。

    您可能还想添加-KILL 或任何您想用来摆脱进程的信号。

    编辑
    正如 ehemient 指出的那样,如果您的程序完成并且其他一些进程抢夺了 pid,那么这里会有一场比赛,它会被杀死。为了减少它发生的可能性,你可以对 SIGCHLD 做出反应,如果发生这种情况,不要试图杀死它。仍然有机会杀死错误的进程,但它非常遥远。

    trapped=""
    trap 'trapped=yes' SIGCHLD
    ./myProgram &
    sleep 1
    [ -z "$trapped" ] && kill $! 2>/dev/null && echo '...'
    

    【讨论】:

    • 可能想要保存PID=$!,设置trap 'PID=' SIGCHLD[ -n $PID ] && kill $pid,以便在(不太可能)您的孩子退出并且另一个进程开始重用相同的PID 的情况下,您不要杀死无辜的旁观者。
    • @ephemient:是的,我也考虑过。不会完全消除比赛,但肯定会缩小窗口。
    • 我其实不认为新过程中有窗口,因为PID只有在原始进程被收割后才能被重用,而这(如果我们没有陷入困境)没有发生还没有。
    • @ephemient:我不确定 bash 究竟是如何处理它的,你可能是对的。问题是它是否可以在产生杀戮进程之后执行陷阱,但在该进程到达杀戮之前......
    • 投反对票,因为来自@wRAR 的回答似乎更加简洁和准确。还有,更清洁。
    【解决方案3】:

    也许 CPU 时间限制 (ulimit -t/setrlimit(RLIMIT_CPU)) 会有所帮助?

    【讨论】:

    • 如何检查程序是否超过运行时间?
    • @Overdeath:你可能需要编写自己的程序forksetrlimitwaitpid,然后检查它是否被 SIGXCPU 杀死。您还可以检查程序的返回值,当它被信号杀死时,bash 通常会将$? 设置为较高的值。不过不知道能不能确定是不是SIGXCPU。
    • @Overdeath 超过softlimit会被SIGXCPU杀死;如果它忽略这一点并达到硬限制,它将被 SIGKILL 杀死。在外壳中,$?如果进程被信号杀死,将为 128+信号;在 C 中,检查 WIFSIGNALED(status)WTERMSIG(status),如果 statuswait* 返回。
    • 这是最好的解决方案。我所做的是将命令包装在括号中,如下所示: (ulimit -t 5; commandToDo args) 这很好地在 5 秒后终止了命令。适用于 Linux 和 OS X。
    【解决方案4】:

    您可以使用 & 在 shell 脚本中启动它

    your_program &
    pid=$!
    sleep 1
    if [ `pgrep $pid` ]
      then
        kill $pid
        echo "killed $pid because it took too long."
    fi
    

    希望你明白这一点,我不确定这是否正确,我的 shell 技能需要一些更新 :)

    【讨论】:

    • 这种方法很危险,因为可能存在竞争条件:如果进程在一秒钟内完成并且另一个进程被分配了它的原始PID怎么办?然后你会杀死错误的进程。
    【解决方案5】:
    tail -f file & pid=$!
    sleep 10
    kill $pid 2>/dev/null && echo '...'
    

    【讨论】:

    • 这种方法很危险,因为可能存在竞争条件:如果进程在 10 秒内完成并且 另一个 进程被分配了其原始 PID,该怎么办?然后你会杀死错误的进程。
    【解决方案6】:

    如果你有资源,你可以在main() 的早期fork() 然后让父进程测量时间并可能杀死子进程。只需使用标准系统调用fork()waitpid()kill(),...也许是一些标准的 Unix 信号处理。不太复杂,但需要一些努力。

    您也可以在 shell 上编写一些脚本,尽管我怀疑它是否会与 1 秒的时间一样准确。

    如果您只想测量时间,请在 shell 上输入time <cmd ...>

    【讨论】:

    • 这是我第一次想到的方式。但我需要程序在不访问源文件的情况下工作。感谢您的回答
    【解决方案7】:

    好的,所以只需编写一个简短的 C 程序,forks,在子级调用 execlp 或类似的东西,测量父级和子级kills 的时间。应该很容易...

    【讨论】:

      猜你喜欢
      • 2021-11-29
      • 2014-11-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-09
      • 2017-05-11
      • 2015-05-17
      • 1970-01-01
      相关资源
      最近更新 更多