Скачиваний:
65
Добавлен:
08.01.2014
Размер:
2.6 Mб
Скачать

6.2.4. Процедуры sigsetjmpи siglongjmp

Иногда при получении сигнала необходимо вернуться на предыдущую позицию в программе. Например, может потребоваться, чтобы пользователь мог вернуться в основное меню программы при нажатии клавиши прерывания. Это можно выполнить, используя две специальные функции sigsetjmp и siglongjmp. (Существуют альтернативные функции setjmp и longjmp, но их нельзя использовать совместно с обработкой сигналов.) Процедура sigsetjmp «сохраняет» текущие значения счетчика команд, позиции стека, регистров процессора и маски сигналов, а процедура siglongjmp передает управление назад в сохраненное таким образом положение. В этом смысле процедура siglongjmp аналогична оператору goto. Важно понимать, что возврат из процедуры siglongjmp не происходит, так как стек возвращается в сохраненное состояние. Как будет показано ниже, при этом происходит выход из соответствующего вызова sigsetjmp.

Описание

uses stdio;

(* Сохранить текущее положение в программе *)

function sigsetjmp(var env:jmp_buf;savemask:longint):integer;

(* Вернуться в сохраненную позицию *)

procedure siglongjmp(var env:jmp_buf;val:integer);

Текущее состояние программы сохраняется в объекте типа sigjmp_buf, определенном в файле stdio. Если во время вызова sigsetjmp значение аргумента savemask не равно нулю, то вызов sigsetjmp сохранит помимо основного контекста программы также текущую маску сигналов (маску блокированных сигналов и действия, связанные со всеми сигналами). Возвращаемое функцией sigsetjmp значение является важным: если в точку sigsetjmp управление переходит из функции siglongjmp, то она возвращает ненулевое значение – аргумент val вызова siglongjmp. Если же функция sigsetjmp вызывается в обычном порядке исполнения программы, то она возвращает значение 0.

Следующий пример демонстрирует технику использования этих функций:

(* Пример использования процедур sigsetjmp и siglongjmp *)

uses linux,stdio;

var

position:sigjmp_buf;

procedure domenu;

var

choice:integer;

begin

write('Choice menu entry:'#$a' menu 1'#$a' menu 2'#$a' menu 3'#$a'?>');

scanf('%d',[@choice]);

end;

procedure goback(smth:longint);cdecl;

begin

fprintf (stderr, #$a'Прерывание'#$a, []);

(* Вернуться в сохраненную позицию *)

siglongjmp (position, 1);

end;

var

act:sigactionrec;

begin

(*

.

.

. *)

(* Сохранить текущее положение *)

if sigsetjmp(position, 1) = 0 then

begin

act.handler.sh := @goback;

sigaction (SIGINT, @act, nil);

end;

domenu;

(*

.

.

. *)

end.

Если пользователь нажимает на клавишу прерывания задания после вызова sigaction, то управление передается в точку, положение которой было записано при помощи функции sigsetjmp. Поэтому выполнение программы продолжается, как если бы только что завершился соответствующий вызов sigsetjmp. В этом случае возвращаемое функцией sigsetjmp значение будет равно второму параметру процедуры siglongjmp.

6.3. Блокирование сигналов

Если программа выполняет важную задачу, такую как обновление базы данных, то может понадобиться ее защита от прерывания на время выполнения таких критических действий. Как уже упоминалось, вместо игнорирования поступающих сигналов процесс может блокировать сигналы, это будет означать, что их выполнение будет отложено до тех пор, пока процесс не завершит выполнение критического участка.

Блокировать определенные сигналы в процессе позволяет системный вызов sigprocmask, определенный следующим образом:

Соседние файлы в папке Полищук, Семериков. Системное программирование в UNIX средствами Free Pascal