Ограничение процесса по времени - Пример



 

Этот полезный пример начинает исполнять какую-либо команду и прерывает ее, если она исполняется дольше заданного времени. Команда, у которой ограничено время исполнения, запускается как порождённый процесс. Будильник устанавливается на заданное время, и затем родительский процесс ожидает, когда порождённый завершится. Если до этого момента возникнет сигнал SIGALRM, то порождённый процесс принудительно завершается сигналом SIGKILL из родительской функции обработки сигнала.

14-18 Команда, за исполнением которой мы наблюдаем, запускается как порождённый процесс.

19-20 Определяется функция обработки, для того чтобы отреагировать на истечение времени будильника. После этого будильник устанавливается на заданный интервал времени.

21   Здесь процесс ждёт завершения порождённого процесса. Если wait(2) будет прерван сигналом, он будет запущен снова, так как он находится внутри цикла while. Когда истекает время, на которое был установлен будильник, вызывается sigalarm(), и wait(2) возвращает -1. Следующий вызов wait(2) в цикле подтверждает смерть порождённого процесса.

22-29 Если wait(2) возвращает неудачу, проверяется, произошло ли это из-за получения сигнала. Иначе считается, что возникла какая-то ошибка, и цикл прерывается.

35-38    Это функция обработки сигнала SIGALRM. Когда возникает сигнал SIGALRM, она убивает порождённый процесс.

Аналогичного эффекта можно было бы достичь проще, установив будильник в порождённом процессе, после fork(2), но перед вызовом exec(2). Но некоторые процессы могут переустанавливать будильник, например, при вызове sleep(3C), и устанавливать обработчики SIGALARM.

 

Файл: timeout.c

          ОГРАНИЧЕНИЕ ПРОЦЕССА ПО ВРЕМЕНИ - ПРИМЕР

 

 1 #include <stdio.h>

 2 #include <sys/types.h>

 3 #include <signal.h>

 4 #include <sys/procset.h>

 5 #include <errno.h>

 6 #include <unistd.h>

 7 #define TIMEOUT 10

 8   static int pid;

 9

10 main(int argc, char **argv)

11 {

12     void sigalarm(int);

13     int status;

14     if ((pid = fork()) == 0) {

15         execvp(argv[1], &argv[1]);

16         perror(argv[1]);

17         exit(127);

18     }

19     signal(SIGALRM, sigalarm);

20     alarm(TIMEOUT);

21     while (wait(&status) == -1) {

22         if (errno == EINTR) {

23            errno = 0;

24            printf("%s: timed out\n", argv[1]);

25            }

26         else {

27            perror(argv[0]);

28            break;

29         }

30     }

31     printf("time remaining: %d\n", alarm(0));

32     exit(WEXITSTATUS(status));

33 }

34

35 void sigalarm(int sig)

36 {

37     sigsend(P_PID, pid, SIGKILL);

38 }

 

Нелокальный goto

Библиотечная функция longjmp(3C) часто описывается как аналог оператора goto для перехода между функциями. Управление может быть передано только в точку, которая была помечена вызовом setjmp(3C). На самом деле, более точным аналогом пары функций setjmp(3C)/longjmp(3C) являются операторы try/throw языков C++ или Java/C#.

setjmp(3), который метит точку для передачи управления, должен получить в качестве параметра буфер для сохранения текущего контекста функции в данной точке. longjmp(3) вызывается с тем же самым буфером в качестве параметра. Исполнение возобновится с положения последнего setjmp(3) для этого буфера. Значение val будет возвращено setjmp(3) после выполнения longjmp(3), и должно быть ненулевым.

Тип jmp_buf представляет собой массив целых чисел, описанный с помощью конструкции typedef. При использовании этого типа в качестве параметра он передаётся как указатель, поэтому, на самом деле, для функции setjmp(3C) он представляет собой выходной параметр, хотя и выглядит как передаваемый по значению. Число элементов в этом массиве зависит от аппаратной архитектуры и от компилятора, точнее от используемого компилятором соглашения о формате стекового кадра.

Функция longjmp(3) возвращает управление к последнему setjmp(3), который заполнил буфер env, используемый как аргумент longjmp. Различие между прямым вызовом setjmp(3) и возвратом к нему через longjmp(3) состоит в том, что возвращаемое значение будет, соответственно, нулевым и ненулевым. Если longjmp(3) получит ноль в качестве val, значение, выданное setjmp(3), будет 1.

Буфер, созданный вызовом setjmp(3C), действителен только «вниз по стеку» от той функции, где он был создан. Иными словами, если вы создали буфер buf в функции foo(), то вызывать longjmp(3C) с этим буфером можно только из тех функций, которые были прямо или косвенно вызваны из foo(). После возврата из foo() буфер становится недействителен и при повторном вызове foo() его необходимо создать заново. Ни при каких обстоятельствах нельзя вызывать longjmp(3C) с буфером, созданным в другой нити.

Нарушение этих требований не контролируется компилятором, но практически неизбежно приводит к разрушению стека вызовов. Обычно такое разрушение приводит к завершению программы по SIGSEGV, но на практике SIGSEGV может быть получен не сразу после разрушения стека; до получения этого сигнала, программа может выполнить практически произвольный участок кода с произвольными значениями параметров и локальных переменных.

При использовании функций setjmp/longjmp в программах на С++, необходимо следить за соответствием используемой версии libc и версии компилятора С++. Так, на Solaris, при использовании longjmp в программах, скомпилированных SunStudio, при longjmp выполняются деструкторы локальных переменных в уничтожаемых стековых кадрах функций, а при компиляции компилятором GNU CC — не выполняются.

Справки: /usr/include/setjmp.h


Дата добавления: 2018-06-27; просмотров: 193; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!