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

Описание

uses linux;

Procedure SigProcMask(How:Integer; SSet,OSSet:PSigSet);

Параметр how сообщает вызову sigpromask, какое действие он должен выполнять. Например, этот параметр может иметь значение SIG_MASK, указывающее, что с этого момента будут блокироваться сигналы, заданные во втором параметр sset, то есть будет произведена установка маски блокирования сигналов. Третий параметр просто заполняется текущей маской блокируемых сигналов – если не нужно ее знать, просто присвойте этому параметру значение nil. Поясним это на примере:

var

set1:sigset_t;

.

.

.

(* Создать полный набор сигналов *)

sigfillset (@set1);

(* Установить блокировку *)

sigprocmask (SIG_SETMASK, @set1, nil);

(* Критический участок кода .. *)

(* Отменить блокировку сигналов *)

sigprocmask (SIG_UNBLOCK, @set1, nil);

Обратите внимание на использование для отмены блокирования сигналов параметра SIG_UNBLOCK. Заметим, что если использовать в качестве первого параметра SIG_BLOCK вместо SIG_SETMASK, то это приведет к добавлению заданных в переменной set сигналов к текущему набору сигналов.

Следующий более сложный пример показывает, как сначала выполняется блокирование всех сигналов во время выполнения особенно важного участка программы, а затем, при выполнении менее критического участка, блокируются только сигналы SIGINT и SIGQUIT.

(* Блокировка сигналов - демонстрирует вызов sigprocmask *)

uses linux,stdio;

var

set1, set2:sigset_t;

begin

(* Создать полный набор сигналов *)

sigfillset (@set1);

(* Создать набор сигналов, не включающий

* сигналы SIGINT и SIGQUIT

*)

sigfillset (@set2);

sigdelset (@set2, SIGINT);

sigdelset (@set2, SIGQUIT);

(* Некритический участок кода ... *)

(* Установить блокировку всех сигналов *)

sigprocmask (SIG_SETMASK, @set1, nil);

(* Более критический участок кода ... *)

(* Блокировка меньшего числа сигналов. *)

sigprocmask (SIG_UNBLOCK, @set2, nil);

(* Менее критический участок кода ... *)

(* Отменить блокировку для всех сигналов *)

sigprocmask (SIG_UNBLOCK, @set1, nil);

end.

Упражнение 6.2. Перепишите процедуру g_exit в примере 4 из раздела 6.2.2 так, чтобы во время ее выполнения игнорировались сигналы SIGINT и SIGQUIT.

6.4. Посылка сигналов

6.4.1. Посылка сигналов другим процессам: вызов kill

Процесс вызывает процедуру sigaction для установки реакции на поступление сигнала. Обратную операцию, посылку сигнала, выполняет системный вызов kill, описанный следующим образом:

Описание

uses linux;

Function Kill(Pid:Longint; Sig:Integer):Integer;

Первый параметр pid определяет процесс или процессы, которым посылается сигнал sig. Обычно pid является положительным числом, и в этом случае он рассматривается как идентификатор процесса. Поэтому следующий оператор

kill(7421, SIGTERM);

означает «послать сигнал SIGTERM процессу с идентификатором 7421». Так как процесс, посылающий сигнал kill, должен знать идентификатор процесса, которому предназначен сигнал, то вызов kill чаще всего используется для обмена между тесно связанными процессами, например, родительским и дочерним. Заметим, что процесс может послать сигнал самому себе.

Существуют некоторые ограничения, связанные с правами доступа. Чтобы можно было послать сигнал процессу, действующий или истинный идентификатор пользователя посылающего процесса должен совпадать с действующим или истинным идентификатором пользователя процесса, которому сигнал адресован. Процессы суперпользователя, как обычно, могут посылать сигналы любым другим процессам. Если непривилегированный пользователь пытается послать сигнал процессу, который принадлежит другому пользователю, то вызов kill завершится неудачей, вернет значение –1 и поместит в переменную linuxerror значение EPERM. (Другие возможные значения ошибок в переменной linuxerror после неудачного вызова kill – это значение Sys_ESRCH, указывающее, что процесс с заданным идентификатором не существует, и Sys_EINVAL, если sig содержит некорректный номер сигнала.)

Параметр pid вызова kill может также принимать определенные значения, которые имеют особый смысл:

  • если параметр pid равен нулю, то сигнал будет послан всем процессам, принадлежащим к той же группе, что и процесс, пославший сигнал, в том числе и самому процессу;

  • если параметр pid равен –1, и действующий идентификатор пользователя является идентификатором суперпользователя, то сигнал посылается всем процессам, истинный идентификатор пользователя которых равен действующему идентификатору пользователя, пославшего сигнал процесса, снова включая и сам процесс, пославший сигнал;

  • если параметр pid равен –1 и действующий идентификатор пользовать является идентификатором суперпользователя, то сигнал посылается всем процессам, кроме определенных системных процессов (последнее исключение относится ко всем попыткам послать сигнал группе процессов, но наиболее важно это в данном случае);

  • и, наконец, если параметр pid меньше нуля, но не равен –1, то сигнал посылается всем процессам, идентификатор группы которых равен модулю pid, включая пославший сигнал процесс, если для него также выполняется это условие.

Следующий пример – программа synchro создает два процесса, которые будут поочередно печатать сообщения на стандартный вывод. Они синхронизирут свою работу, посылая друг другу сигнал SIGUSR1 при помощи вызова kill.

(* Программа synchro -- пример использования вызова kill *)

uses linux,stdio;

const

ntimes:integer=0;

procedure p_action(sig:integer);cdecl;

begin

inc(ntimes);

writeln ('Родительский процесс получил сигнал ', ntimes, ' раз(а)');

end;

procedure c_action(sig:integer);cdecl;

begin

inc(ntimes);

writeln ('Дочерний процесс получил сигнал ', ntimes, ' раз(а)');

end;

var

pid, ppid:longint;

pact, cact:sigactionrec;

begin

(* Задать обработчик сигнала SIGUSR1 в родительском процессе *)

pact.handler.sh := @p_action;

sigaction (SIGUSR1, @pact, nil);

pid := fork;

case pid of

-1: (* ошибка *)

begin

perror ('synchro');

halt(1);

end;

0: (* дочерний процесс *)

begin

(* Задать обработчик в дочернем процессе *)

cact.handler.sh := @c_action;

sigaction (SIGUSR1, @cact, nil);

(* Получить идентификатор родительского процесса *)

ppid := getppid;

while true do

begin

sleep (1);

kill (ppid, SIGUSR1);

pause;

end;

(* Бесконечный цикл *)

end;

else (* родительский процесс *)

while true do

begin

pause;

sleep (1);

kill (pid, SIGUSR1);

end;

(* Бесконечный цикл *)

end;

end.

Оба процесса выполняют бесконечный цикл, приостанавливая работу до получения сигнала от другого процесса. Они используют для этого системный вызов pause, который просто приостанавливает работу до получения сигнала (см. раздел 6.4.3). Затем каждый из процессов выводит сообщение и, в свою очередь, посылает сигнал при помощи вызова kill. Дочерний процесс начинает вывод сообщений (обратите внимание на порядок операторов в каждом цикле). Оба процесса завершают работу, когда пользователь нажимает на клавишу прерывания. Диалог с программой может выглядеть примерно так:

$ synchro

Родительский процесс получил сигнал #1

Дочерний процесс получил сигнал #1

Родительский процесс получил сигнал #2

Дочерний процесс получил сигнал #2

< прерывание > (пользователь нажал на клавишу прерывания)

$

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