Setjmp(3C) и longjmp(3C) - Пример



Эта программа демонстрирует, каким образом setjmp(3C) и longjmp(3C) можно использовать при обработке сигналов для того, чтобы воздействовать на исполнение основного потока управления программы. Эта программа представляет собой циклическое меню внутри функции main. Вызов setjmp(3C) используется для того, чтобы пометить точку в программе непосредственно перед циклом.

Меню в примере содержит следующие команды:

 s печатать квадраты целых чисел. Исполнение этой команды само по себе не заканчивается, и её необходимо прерывать.

t печатать дату и время

q выйти из программы

Для каждой буквы-команды вызывается определенная функция, выполняющая требуемую операцию. Если пользователь нажимает <Ctrl-C>, программа продолжается с точки, помеченной setjmp, так как функция обработки сигнала вызывает longjmp(3), вместо возврата к месту, где находилось управление до перехвата сигнала.

2-3 Включить <setjmp.h> и объявить переменную env типа jmp_buf.

8 Сигналы прерывания будут перехватываться и обрабатываться заданной пользователем функцией ljmain.

9 Текущая точка программы помечена setjmp(3). Возвращаемое значение будет нулевым. Оно станет ненулевым при последующих переходах на эту точку при вызове longjmp.

12 Вход в цикл меню.

15-26 Меню вызывает функции, выполняющие требуемые команды.

28-33   Функция обработки сигнала SIGINT переустанавливает сигнал на ту же функцию (т.е. на себя), печатает "INTERRUPTED" и выполняет longjmp обратно в функцию main.

...  Не показаны  sfn(), которая печатает последовательность квадратов, и tfn(), печатающая дату и время.

Возможно несколько вызовов setjmp(3), позволяющие делать longjmp(3) в различные точки. Однако, каждый вызов требует своей собственной переменной jmp_buf.

Обратите внимание, что в этой программе обработчик устанавливается вызовом signal(2). Дело в том, что при установке обработчика через sigset(2), сигнал блокируется до возврата из обработчика. Но при вызове longjmp(3C) из обработчика, возврата из обработчика не происходит, и сигнал так и останется заблокированным. Для решения этой проблемы, совместно с sigset(2) необходимо использовать функции sigsetjmp(3C)/siglongjmp(3C), которые в нашем курсе подробно не изучаются.

Этот пример демонстрируется следующим образом:

$ setjmp

cmd: s

0 14916253649

64 81100121144169225

<DELETE>

INTERRUPTED

Before loop

cmd: q

$

 

Файл: setjmp.c

             setjmp(3C) И longjmp(3C) - ПРИМЕР

 

 

 1 #include <signal.h>

 2 #include <setjmp.h>

 3 jmp_buf env1;

 4 void ljmain(int);

 5

 6 main() {

 7       int retcode;

 8       signal(SIGINT, ljmain);

 9       retcode = setjmp(env1);

10       if (retcode != 0) printf("\nBefore loop\n");

11

12       while( 1 ) menu();

13 }

14

15 menu() {

16        char resp[2];

17 prompt: printf("cmd: ");

18        scanf("%s",resp);

19        switch(resp[0]){

20                case 's': sfn(); break;

21                case 't': tfn(); break;

22                case 'q': exit(0);

23                default: printf("?\n");

24                          goto prompt;

25        }

26 }

27

28 void ljmain(int sig)

29 {

30          signal(SIGINT, ljmain);

31        printf("\nINTERRUPTED\n");

32        longjmp(env1, 1);

33 }

34

...

Задержка процесса до сигнала

Системный вызов  pause(2)  задерживает  исполнение  процесса  на неопределённое время до момента, пока не возникнет неигнорируемый сигнал.

В традиционных Unix-системах, pause(2) используется вместе с alarm(2) для реализации библиотечной функции sleep(3C). Если прекращение pause(2) было вызвано сигналом, отличным от SIGALRM, необходимо сбросить будильник вызовом alarm(2) с нулевым аргументом. Иначе, ваша программа будет вести себя непредсказуемым образом из-за получения "дополнительного" сигнала.  Заметьте, что alarm(2) может быть использован вместе с другими блокирующимися системными вызовами, такими как read(2) или wait(2).

 

Задержка исполнения на заданный промежуток времени - Пример

Этот пример — упрощенная версия функции sleep(3C) в традиционной версии библиотеки libc. Он демонстрирует совместное использование системных вызовов alarm(2) и pause(2). longjmp(3) используется для возврата из функции обработки сигнала.

В многопоточной версии libc такая реализация sleep(3C) недопустима, ведь процесс имеет только один будильник, и его переустановка в разных потоках приводила бы к конфликтам. Поэтому, в Solaris 9 и последующих версиях, функция sleep(3C) реализована через специальный системный вызов. Тем не менее, данный пример интересен тем, что показывает способ обхода так называемой «ошибки потерянного пробуждения» (lost wakeup error).

12 Устанавливается функция обработки для сигнала SIGALRM. Предыдущая установка реакции запоминается.

13 Если это был прямой вызов setjmp(3), то выполняются операторы внутри if. Иначе, если мы вернулись сюда через longjmp(3), возвращаемое значение будет ненулевым, и тело оператора if выполняться не будет.

14-15  Эти строки кода устанавливают интервал времени. setjmp(3) и longjmp(3) используются, потому что после установки будильника существует вероятность, что сигнал возникнет до вызова pause(2). Это может произойти при большой загрузке системы или, например, если ноутбук с системой будет переведён в спящий режим. В этом случае, процесс будет задержан навсегда. При использовании longjmp(3) в функции обработки сигнала, передача управления назад к setjmp(3) происходит всегда, независимо от того, была вызвана pause(2) или нет.

17        Если переменная unslept положительна, значит возврат из pause(2) был вызван каким-то другим сигналом, а не SIGALRM. Кроме того, будильник выключается, чтобы предотвратить нежелательный сигнал SIGALRM. Таким образом, эта строка служит не только для получения количества "недоспанного" времени.

23-27 Это функция обработки сигнала SIGALRM. Заметьте, что это статическая функция, так что она будет локальной в этом файле исходного кода. Передача управления назад к setjmp(3) обсуждалась выше.

 

Эта программа тестируется следующим драйвером:

30     #ifdef DEBUG

31     main(int argc, char **argv)

32     {

33     int unslept, mysleep(int);

34     void sigint();

35

36     signal(SIGINT, sigint);

37     unslept = mysleep(atoi(argv[1]));

38     printf("remaining time: %d\n", unslept);

39     }

40

41  void sigint() {}

42 #endif

 

Файл: mysleep.c


ЗАДЕРЖКА ИСПОЛНЕНИЯ НА ЗАДАННЫЙ ПРОМЕЖУТОК ВРЕМЕНИ - ПРИМЕР

 

 1   #include <signal.h>

 2   #include <setjmp.h>

 3   #include <stdlib.h>

 4

 5   static jmp_buf env;

 6

 7   mysleep(int seconds)

 8   {

 9   void sigcatch(int), (*astat)(int);

10   int unslept = seconds;

11

12   astat = signal(SIGALRM, sigcatch);

13   if (setjmp(env) == 0) {

14   alarm(seconds);

15   pause();

16   }

17   unslept = alarm(0);

18   signal(SIGALRM, astat);

19   return(unslept);

20   }

21

22

23   static void sigcatch(int sig)

24   {

25   longjmp(env, 1);

26   }

27

 


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

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






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