문제

일정 시간이 지나면 자동으로 명령을 죽이고 싶습니다. 다음과 같은 인터페이스를 염두에두고 있습니다.

% constrain 300 ./foo args

"args"와 함께 "./foo"가 작동하지만 5 분 후에도 여전히 실행되면 자동으로 죽입니다.

너무 많은 메모리를 사용하는 경우 Autokilling A 프로세스와 같은 다른 제약으로 아이디어를 일반화하는 것이 유용 할 수 있습니다.

그렇게하는 기존 도구가 있습니까, 아니면 그런 것을 쓴 사람이 있습니까?

추가 : Jonathan의 솔루션은 정확히 내가 생각했던 것이며 Linux의 매력처럼 작동하지만 Mac OSX에서 작업 할 수는 없습니다. Sigrtmin을 제거하여 정상으로 컴파일 할 수 있지만 신호는 아동 프로세스로 보내지 않습니다. Mac 에서이 작업을 수행하는 방법을 아는 사람이 있습니까?

추가 : Mac과 다른 곳에서 작동하는 Jonathan에서 업데이트를 사용할 수 있습니다.

도움이 되었습니까?

해결책

나는이 파티에 오히려 늦게 도착했지만 답에 내가 가장 좋아하는 트릭이 보이지 않습니다.

*nix, an alarm(2) AN을 가로 질러 상속됩니다 execve(2) Sigalrm은 기본적으로 치명적입니다. 따라서 종종 간단히 할 수 있습니다.

$ doalarm () { perl -e 'alarm shift; exec @ARGV' "$@"; } # define a helper function

$ doalarm 300 ./foo.sh args

또는 설치 a 사소한 C 래퍼 당신을 위해 그렇게합니다.

장점 하나의 PID 만 관련되어 있으며 메커니즘은 간단합니다. 예를 들어, 당신은 잘못된 과정을 죽이지 않을 것입니다. ./foo.sh "너무 빨리"종료되었고 PID가 재사용되었습니다. 콘서트에서 작동하는 몇 개의 쉘 하위 프로세스가 필요하지 않으며, 올바르게 수행 할 수는 있지만 오히려 레이스가 발생하기 쉽습니다.

단점 시간이 제한된 프로세스는 알람 시계를 조작 할 수 없습니다 (예 : alarm(2), ualarm(2), setitimer(2)), 이것은 상속 된 경보를 제거 할 가능성이 있기 때문입니다. 분명히 Sigalrm을 차단하거나 무시할 수는 없지만 Sigint, Sigterm 등에 대해서도 동일하게 말할 수 있습니다. 다른 접근법.

일부 (아주 오래된) 시스템 구현 sleep(2) 측면에서 alarm(2), 그리고 오늘날에도 일부 프로그래머는 사용합니다 alarm(2) I/O 및 기타 작업을위한 조잡한 내부 시간 초과 메커니즘으로. 그러나 내 경험상,이 기술은 시간 제한을 원하는 대부분의 프로세스에 적용 할 수 있습니다.

다른 팁

GNU Coreutils가 포함됩니다 시간 초과 많은 시스템에서 기본적으로 설치된 명령.

https://www.gnu.org/software/coreutils/manual/html_node/timeout-invocation.html

시청합니다 free -m 1 분 동안 용어 신호를 보내서 죽여야합니다.

timeout 1m watch free -m

어쩌면 나는 질문을 이해하지 못할 수도 있지만, 적어도 Bash에서 직접 할 수 있습니다.

( /path/to/slow command with options ) & sleep 5 ; kill $!

이것은 괄호 안에있는 첫 번째 명령을 5 초 동안 실행 한 다음 죽인다. 전체 작업은 동시에 실행됩니다. 즉, 느린 명령을 기다리는 동안 바쁘게 쉘을 사용할 수 없습니다. 그것이 당신이 원했던 것이 아니라면, 다른 것을 추가 할 수 있어야합니다.

그만큼 $! 변수는 가장 최근에 시작된 서브 쉘의 프로세스 ID를 포함하는 Bash 내장입니다. 괄호 안에 & 안에는 그렇지 않으므로 프로세스 ID를 잃는 것이 중요합니다.

나는 프로그램이 있습니다 timeout C는 원래 1989 년에 작성되었지만 그 이후로 정기적으로 업데이트되었습니다.


업데이트 : Sigrtmin이 정의되지 않았기 때문에이 코드는 MacOS X에서 컴파일하지 못하고 MacOS X에서 실행될 때 타임 아웃에 실패합니다. 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() in <unistd.h> on Solaris 7 */
#define _XOPEN_SOURCE 500

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#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

프로세스를 CPU 시간의 10 초로 제한합니다.

실제로이를 사용하여 현재 프로세스가 아닌 새 프로세스를 제한하려면 래퍼 스크립트를 사용할 수 있습니다.

#! /usr/bin/env python

import os
os.system("ulimit -t 10; other-command-here")

다른 명령은 모든 도구 일 수 있습니다. Java, Python, C 및 다양한 분류 알고리즘의 체계 버전을 실행하고 실행 시간을 30 초로 제한하는 동안 얼마나 오래 걸렸는지 로깅을했습니다. Cocoa -Python 애플리케이션은 인수를 포함하여 다양한 명령 줄을 생성하고 Times를 CSV 파일로 만듭니다. 그러나 실제로 위에서 제공 한 명령 위에는 보풀이있었습니다.

킥을위한 Perl One Liner :

perl -e '$s = shift; $SIG{ALRM} = sub { print STDERR "Timeout!\n"; kill INT => $p }; exec(@ARGV) unless $p = fork; alarm $s; waitpid $p, 0' 10 yes foo

이이 이것은 10 초 동안 'foo'를 인쇄 한 다음 시간을 끄는다. '10'을 몇 초로 교체하고 명령으로 '예 yes foo'를 교체하십시오.

Mac에서 작업하기 위해 소스에서 컴파일 된 경우 Ubuntu/Debian의 타임 아웃 명령. 다윈

10.4.*

http://packages.ubuntu.com/lucid/timeout

Perl One-Liner에 대한 저의 변형은 Fork () 및 Wait ()를 사용하지 않고 잘못된 과정을 죽일 위험없이 출구 상태를 제공합니다.

#!/bin/sh
# Usage: timelimit.sh secs cmd [ arg ... ]
exec perl -MPOSIX -e '$SIG{ALRM} = sub { print "timeout: @ARGV\n"; kill(SIGTERM, -$$); }; alarm shift; $exit = system @ARGV; exit(WIFEXITED($exit) ? WEXITSTATUS($exit) : WTERMSIG($exit));' "$@"

기본적으로 Fork () 및 Wait ()는 System () 내부에 숨겨져 있습니다. SIGALRM은 부모 프로세스로 전달 된 후 전체 프로세스 그룹 (-$$)에 SIGTERM을 보내서 스스로와 자녀를 죽입니다. 어린이가 킬이 나오기 전에 자녀가 나가고 자녀의 PID가 재사용 될 가능성이 거의없는 경우에, 이는 노인 PID를 사용한 새로운 프로세스가 상위 Perl 프로세스의 동일한 프로세스 그룹에 있지 않기 때문에 잘못된 과정을 죽이지 않을 것입니다. .

추가 혜택으로 스크립트는 또한 아마 올바른 출구 상태.

다음과 같은 것을 시도하십시오.

# This function is called with a timeout (in seconds) and a pid.
# After the timeout expires, if the process still exists, it attempts
# to kill it.
function timeout() {
    sleep $1
    # kill -0 tests whether the process exists
    if kill -0 $2 > /dev/null 2>&1 ; then
        echo "killing process $2"
        kill $2 > /dev/null 2>&1
    else
        echo "process $2 already completed"
    fi
}

<your command> &
cpid=$!
timeout 3 $cpid
wait $cpid > /dev/null 2>&
exit $?

프로세스 'PID가 타임 아웃 내에 재사용되면 잘못된 프로세스를 죽일 수 있다는 단점이 있습니다. 이것은 거의 가능하지 않지만 초당 200000+ 프로세스를 시작할 수 있습니다. 이것은 고정 될 수 있습니다.

데비안 저장소에서 사용할 수있는 패키지 인 "Timelimit"을 사용합니다.

http://devel.ringlet.net/sysutils/timelimit/

#!/bin/sh
( some_slow_task ) & pid=$!
( sleep $TIMEOUT && kill -HUP $pid ) 2>/dev/null & watcher=$!
wait $pid 2>/dev/null && pkill -HUP -P $watcher

감시자는 시간 초과가 주어진 후 느린 작업을 죽인다. 스크립트는 느린 작업을 기다리고 감시자를 종료합니다.

예 :

  • 느린 작업은 2 초 이상 실행되어 종료되었습니다.

느린 작업이 중단되었습니다

( sleep 20 ) & pid=$!
( sleep 2 && kill -HUP $pid ) 2>/dev/null & watcher=$!
if wait $pid 2>/dev/null; then
    echo "Slow task finished"
    pkill -HUP -P $watcher
    wait $watcher
else
    echo "Slow task interrupted"
fi
  • 이 느린 작업은 주어진 시간 초과 전에 완료되었습니다

느린 작업이 완료되었습니다

( sleep 2 ) & pid=$!
( sleep 20 && kill -HUP $pid ) 2>/dev/null & watcher=$!
if wait $pid 2>/dev/null; then
    echo "Slow task finished"
    pkill -HUP -P $watcher
    wait $watcher
else
    echo "Slow task interrupted"
fi

Perl One-Liner를 약간 수정하면 출구 상태가 올바르게됩니다.

perl -e '$s = shift; $SIG{ALRM} = sub { print STDERR "Timeout!\n"; kill INT => $p; exit 77 }; exec(@ARGV) unless $p = fork; alarm $s; waitpid $p, 0; exit ($? >> 8)' 10 yes foo

기본적으로 종료 ($? >> 8)는 하위 프로세스의 종료 상태를 전달합니다. 방금 타임 아웃 출구 상태에서 77을 선택했습니다.

기대 도구를 사용하는 것은 어떻습니까?

## run a command, aborting if timeout exceeded, e.g. timed-run 20 CMD ARGS ...
timed-run() {
  # timeout in seconds
  local tmout="$1"
  shift
  env CMD_TIMEOUT="$tmout" expect -f - "$@" <<"EOF"
# expect script follows
eval spawn -noecho $argv
set timeout $env(CMD_TIMEOUT)
expect {
   timeout {
      send_error "error: operation timed out\n"
      exit 1
   }
   eof
}
EOF
}

순수한 배쉬 :


#!/bin/bash

if [[ $# < 2 ]]; then
  echo "Usage: $0 timeout cmd [options]"
  exit 1
fi

TIMEOUT="$1"
shift

BOSSPID=$$

(
  sleep $TIMEOUT
  kill -9 -$BOSSPID
)&
TIMERPID=$!

trap "kill -9 $TIMERPID" EXIT

eval "$@"

이 작업을 수행하기 위해 "at"으로 특정 시간을 설정할 수있는 방법이 없습니까?

$ at 05:00 PM kill -9 $pid

훨씬 간단 해 보입니다.

PID 번호가 무엇인지 모르는 경우 PS AUX 및 GREP로 읽는 스크립트가 있지만이를 구현하는 방법을 잘 모르겠습니다.

$   | grep someprogram
tony     11585  0.0  0.0   3116   720 pts/1    S+   11:39   0:00 grep someprogram
tony     22532  0.0  0.9  27344 14136 ?        S    Aug25   1:23 someprogram

스크립트는 PID를 읽고 변수를 할당해야합니다. 나는 지나치게 숙련되지는 않지만 이것이 가능하다고 가정합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top