我想在一段时间后自动杀死一个命令.我记得这样的界面:
% constrain 300 ./foo args
哪个会用"args"运行"./foo"但如果它在5分钟后仍在运行则会自动终止它.
将该想法概括为其他约束可能是有用的,例如如果它使用过多内存则自动填充进程.
是否有任何现有的工具可以做到这一点,或者有人写过这样的东西?
补充:Jonathan的解决方案正是我的想法,它在linux上的功能就像一个魅力,但我不能让它在Mac OSX上运行.我摆脱了SIGRTMIN,它让它编译得很好,但信号不会被发送到子进程.有人知道如何在Mac上完成这项工作吗?
[补充:请注意,Jonathan可以在Mac和其他地方使用更新.]
GNU Coreutils包含timeout命令,默认安装在许多系统上.
https://www.gnu.org/software/coreutils/manual/html_node/timeout-invocation.html
要观察free -m
一分钟,然后通过发送一个TERM信号来杀死它:
timeout 1m watch free -m
也许我不理解这个问题,但这听起来很直接,至少在bash中是这样:
( /path/to/slow command with options ) & sleep 5 ; kill $!
这将在括号内运行第一个命令五秒钟,然后将其杀死.整个操作同步运行,即在忙于等待慢速命令时,您将无法使用shell.如果那不是你想要的,应该可以添加另一个&.
该$!
变量是Bash内置函数,包含最近启动的子shell的进程ID.重要的是不要在括号内使用&,这样做会丢失进程ID.
我来这个派对已经很晚了,但我没有看到答案中列出我最喜欢的技巧.
在*NIX下,一个alarm(2)
是继承的,execve(2)
并且SIGALRM默认是致命的.所以,你通常可以简单地说:
$ doalarm () { perl -e 'alarm shift; exec @ARGV' "$@"; } # define a helper function $ doalarm 300 ./foo.sh args
或者安装一个简单的C包装器来为你做这件事.
优点只涉及一个PID,机制很简单.例如,如果./foo.sh
退出"太快"并重新使用其PID ,则不会杀死错误的进程.您不需要多个shell子进程协同工作,这可以正确完成,但更容易出现竞争.
缺点的时间约束过程中不能操纵它的闹钟(例如alarm(2)
,ualarm(2)
,setitimer(2)
),因为这可能会清除继承报警.显然,它也不能阻止或忽略SIGALRM,尽管对于其他一些方法也可以说SIGINT,SIGTERM等.
一些(很老的,我认为)系统实现sleep(2)
了alarm(2)
,甚至在今天,一些程序员使用alarm(2)
作为I/O和其他操作的粗略内部超时机制.但是,根据我的经验,这种技术适用于您想要限时的绝大多数流程.
我有一个叫做的程序timeout
- 用C语言编写,最初是在1989年,但从那时起定期更新.
signal()
函数wait()
在警报超时后恢复- 这不是必需的行为.我有一个新版本timeout.c
处理这两个问题(使用sigaction()
而不是signal()
).和以前一样,请联系我获取10K gzipped tar文件,其中包含源代码和手册页(请参阅我的个人资料).
/* @(#)File: $RCSfile: timeout.c,v $ @(#)Version: $Revision: 4.6 $ @(#)Last changed: $Date: 2007/03/01 22:23:02 $ @(#)Purpose: Run command with timeout monitor @(#)Author: J Leffler @(#)Copyright: (C) JLSS 1989,1997,2003,2005-07 */ #define _POSIX_SOURCE /* Enable kill() inon Solaris 7 */ #define _XOPEN_SOURCE 500 #include #include #include #include #include #include #include #include "stderr.h" #define CHILD 0 #define FORKFAIL -1 static const char usestr[] = "[-vV] -t time [-s signal] cmd [arg ...]"; #ifndef lint /* Prevent over-aggressive optimizers from eliminating ID string */ const char jlss_id_timeout_c[] = "@(#)$Id: timeout.c,v 4.6 2007/03/01 22:23:02 jleffler Exp $"; #endif /* lint */ static void catcher(int signum) { return; } int main(int argc, char **argv) { pid_t pid; int tm_out; int kill_signal; pid_t corpse; int status; int opt; int vflag = 0; err_setarg0(argv[0]); opterr = 0; tm_out = 0; kill_signal = SIGTERM; while ((opt = getopt(argc, argv, "vVt:s:")) != -1) { switch(opt) { case 'V': err_version("TIMEOUT", &"@(#)$Revision: 4.6 $ ($Date: 2007/03/01 22:23:02 $)"[4]); break; case 's': kill_signal = atoi(optarg); if (kill_signal <= 0 || kill_signal >= SIGRTMIN) err_error("signal number must be between 1 and %d\n", SIGRTMIN - 1); break; case 't': tm_out = atoi(optarg); if (tm_out <= 0) err_error("time must be greater than zero (%s)\n", optarg); break; case 'v': vflag = 1; break; default: err_usage(usestr); break; } } if (optind >= argc || tm_out == 0) err_usage(usestr); if ((pid = fork()) == FORKFAIL) err_syserr("failed to fork\n"); else if (pid == CHILD) { execvp(argv[optind], &argv[optind]); err_syserr("failed to exec command %s\n", argv[optind]); } /* Must be parent -- wait for child to die */ if (vflag) err_remark("time %d, signal %d, child PID %u\n", tm_out, kill_signal, (unsigned)pid); signal(SIGALRM, catcher); alarm((unsigned int)tm_out); while ((corpse = wait(&status)) != pid && errno != ECHILD) { if (errno == EINTR) { /* Timed out -- kill child */ if (vflag) err_remark("timed out - send signal %d to process %d\n", (int)kill_signal, (int)pid); if (kill(pid, kill_signal) != 0) err_syserr("sending signal %d to PID %d - ", kill_signal, pid); corpse = wait(&status); break; } } alarm(0); if (vflag) { if (corpse == (pid_t) -1) err_syserr("no valid PID from waiting - "); else err_remark("child PID %u status 0x%04X\n", (unsigned)corpse, (unsigned)status); } if (corpse != pid) status = 2; /* Dunno what happened! */ else if (WIFEXITED(status)) status = WEXITSTATUS(status); else if (WIFSIGNALED(status)) status = WTERMSIG(status); else status = 2; /* Dunno what happened! */ return(status); }
如果你想要'stderr.h'和'stderr.c'的'官方'代码,请联系我(参见我的个人资料).
还有ulimit,可用于限制子进程可用的执行时间.
ulimit -t 10
将进程限制为10秒的CPU时间.
要实际使用它来限制新进程而不是当前进程,您可能希望使用包装器脚本:
#! /usr/bin/env python import os os.system("ulimit -t 10; other-command-here")
other-command可以是任何工具.我正在运行不同排序算法的Java,Python,C和Scheme版本,并记录它们花了多长时间,同时将执行时间限制为30秒.Cocoa-Python应用程序生成了各种命令行 - 包括参数 - 并将时间整理成CSV文件,但它实际上只是在上面提供的命令之上.