Смекни!
smekni.com

Теория вычислительных процессов и структур (стр. 6 из 8)

SIGTTIN – сигнал о попытке ввода с терминала фоновым процессом (background process attempting read). Если процесс выполняется в фоновом режиме и пытается выполнить чтение с управляющего терминала, то ему посылается этот сигнал. Действие сигнала по умолчанию – остановка процесса;

SIGTTOU – сигнал о попытке вывода на терминал фоновым процессом (background process attempting write). Аналогичен сигналу SIGTTIN, но генерируется, если фоновый процесс пытается выполнить запись в управляющий терминал. Действие сигнала по умолчанию – остановка процесса;

SIGURG – сигнал о поступлении в буфер сокета срочных данных (high bandwidth data is available at a socket). Он сообщает процессу, что по сетевому соединению получены срочные внеочередные данные;

SIGUSR1 и SIGUSR2 – пользовательские сигналы (user defined signals 1 and 2). Так же, как и сигнал SIGTERM, эти сигналы никогда не посылаются ядром и могут использоваться для любых целей по выбору пользователя;

SIGVTALRM – сигнал виртуального таймера (virtual timer expired). Третий таймер можно установить так, чтобы он измерял время, которое процесс выполняет в пользовательском режиме;

SIGXCPU – сигнал о превышении лимита процессорного времени (CPU time limit exceeded). Он посылается процессу, если суммарное процессорное время, занятое его работой, превысило установленный предел. Действие по умолчанию – аварийное завершение;

SIGXFSZ – сигнал о превышении предела на размер файла (file size limit exceeded). Он генерируется, если процесс превысит максимально допустимый размер файла.

При получении сигнала процесс может выполнить одно из трех действий. Первое – действие по умолчанию. Оно заключается в прекращении выполнения процесса, а для некоторых сигналов – в игнорировании сигнала либо в остановке процесса. Второе действие – игнорировать сигнал и продолжать выполнение. Третье – выполнить определенное пользователем действие.

Наборы сигналов являются одним из основных параметров, передаваемых работающим с сигналами системным вызовам. Они просто задают список сигналов, которые необходимо передать системному вызову.

Наборы сигналов определяются при помощи типа sigset_t, который определен в заголовочном файле <signal.h>. Размер типа задан так, чтобы в нем мог поместиться весь набор определенных в системе сигналов. Выбрать определенные сигналы можно, начав либо с полного набора сигналов и удалив ненужные сигналы, либо с пустого набора, включив в него нужные. Инициализация пустого и полного набора сигналов выполняется при помощи процедур sigemptyset и sigfillset соответственно. После инициализации с наборами сигналов можно оперировать при помощи процедур sigaddset и sigdelset, соответственно добавляющих и удаляющих указанные вами сигналы.

Описание данных процедур:

#include <signal.h>

/* Инициализация*/

int sigemptyset (sigset_t *set);

int sigfillset (sigset_t *set);

/*Добавление и удаление сигналов*/

int sigaddset (sigset_t *set, int signo);

int sigdelset (sigset_t *set, int signo);

Процедуры sigemptyset и sigfillset имеют единственный параметр – указатель на переменную типа sigset_t. Вызов sigemptyset инициализирует набор set, исключив из него все сигналы. И наоборот, вызов sigfillset инициализирует набор, на который указывает set, включив в него все сигналы. Приложения должны вызывать sigemptyset или sigfillset хотя бы один раз для каждой переменной типа sigset_t.

Процедуры sigaddset и sigdelset принимают в качестве параметров указатель на инициализированный набор сигналов и номер сигнала, который должен быть добавлен или удален. Второй параметр, signo, может быть символическим именем константы, таким как SIGINT, или настоящим номером сигнала, но в последнем случае программа окажется системно-зависимой.

После определения списка сигналов можно задать определенный метод обработки сигнала при помощи процедуры sigaction:

#include <signal.h>

int sigaction (int signo, const struct sigaction *act,

struct sigaction *oact);

Первый параметр signo задает отдельный сигнал, для которого нужно определить действие. Чтобы это действие выполнялось, процедура sigaction должна быть вызвана до получения сигнала типа signo. Значение переменной signo может быть любое из ранее определенных имен сигналов, за исключением SIGSTOP и SIGKILL, которые предназначены только для остановки или завершения процесса и не могут обрабатываться по-другому.

Второй параметр, act, определяет обработчика сигнала signo. Третий параметр, oact, если не равен NULL, указывает на структуру, куда будет помещено описание старого метода обработки сигнала. Рассмотрим структуру sigaction, определенную в файле <signal.h>:

struct sigaction {

void (*sa_handler) (int); /*Функция обработчика*/

sigset_t sa_mask, /*Сигналы, которые блокируются

во время обработки сигнала*/

int sa_flags; /*Флаги, влияющие на поведение сигнала*/

void (*sa_sigaction) (int, siginfo_t *, void *);

/*Указатель на обработчик сигналов*/

};

Первое поле, sa_handler, задает обработчик сигнала signo. Это поле может иметь три вида значений. Первое – SIG_DFL – константа, сообщающая, что нужно восстановить обработку сигнала по умолчанию. Второе – SIG_IGN – константа, означающая, что нужно игнорировать данный сигнал. Не может использоваться для сигналов SIGSTOP и SIGKILL. Третье – адрес функции, принимающей аргумент типа int. Если функция объявлена в тексте программы до заполнения sigaction, то полю sa_handler можно просто присвоить имя функции. Компилятор поймет, что имелся в виду ее адрес. Эта функция будет выполняться при получении сигнала signo, а само значение signo будет передано в качестве аргумента вызываемой функции. Управление будет передано функции, как только процесс получит сигнал, какой бы участок программы при этом ни выполнялся. После возврата из функции управление будет снова передано процессу и продолжится с точки, в которой выполнение процесса было прервано.

Второе поле, sa_mask, демонстрирует первое практическое использование набора сигналов. Сигналы, заданные в этом поле, будут блокироваться во время выполнения функции, заданной полем sa_handler. Это не означает, что эти сигналы будут игнорироваться, просто их обработка будет отложена до завершения функции. При входе в функцию перехваченный сигнал также будет неявно добавлен к текущей маске сигналов.

Поле sa_flags может использоваться для изменения характера реакции на сигнал signo.

Пример перехвата сигнала SIGINT демонстрирует, как можно перехватить сигнал, а также проясняет лежащий в его основе механизм сигналов. Программа sigex просто связывает с сигналом SIGINT функцию catchint, а затем выполняет набор операторов sleep и printf. В данном примере определена структура act типа sigaction как static, поэтому при инициализации структуры все поля, в частности поле sa_flags, обнуляются:

#include <signal.h>

main()

{

static struct sigaction act;

/*Определение процедуры обработчика сигнала catchint*/

void catchint (int);

/*Задание действия при получении сигнала SIGINT*/

act.sa_handler = catchint;

/*Создать маску, включающую все сигналы*/

sigfillset (& (act.sa_mask));

/*До вызова процедуры sigaction сигнал SIGINT*/

/*приводил к завершению процесса (действие по умолчанию).*/

sigaction (SIGINT, &act, NULL);

/*При получении сигнала SIGINT управление*/

/*будет передаваться процедуре catchint*/

printf (“Вызов sleep номер 1&bsol;n”);

sleep (1);

printf (“Вызов sleep номер 2&bsol;n”);

sleep (1);

printf (“Вызов sleep номер 3&bsol;n”);

sleep (1);

printf (“Вызов sleep номер 4&bsol;n”);

sleep (1);

printf (“Выход&bsol;n”);

exit (0);

}

/*Простая функция для обработки сигнала SIGINT*/

void catchint (int signal)

{

printf (“&bsol;nСигнал CATCHINT: signo = %d&bsol;n”, signo);

printf (“Сигнал CATCHINT: возврат&bsol;n&bsol;n”);

}

Сеанс обычного запуска sigex будет выглядеть так:

$ sigex

Вызов sleep номер 1

Вызов sleep номер 2

Вызов sleep номер 3

Вызов sleep номер 4

Выход

Пользователь может прервать выполнение данной программы, нажав клавишу прерывания задания. Если она была нажата до того, как в программе была выполнена процедура sigaction, то процесс просто завершит работу. Если же нажать на клавишу прерывания после вызова, то управление будет передано функции catchint:

$ sigex

Вызов sleep номер 1

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

Сигнал CATCHINT : signo =2

Сигнал CATCHINT : возврат

Вызов sleep номер 2

Вызов sleep номер 3

Вызов sleep номер 4

Выход

Обратите внимание на то, как передается управление из тела программы в процедуру catchint. После завершения этой процедуры, управление продолжится с точки, в которой программа была прервана. Можно попробовать прервать программу и в другом месте:

$ sigex

Вызов sleep номер 1

Вызов sleep номер 2

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

Сигнал CATCHINT : signo =2

Сигнал CATCHINT : возврат

Вызов sleep номер 3

Вызов sleep номер 4

Выход

Для того чтобы процесс игнорировал сигнал прерывания SIGINT, нужно заменить строку в программе:

act.sa_handler = catchint;

на

act.sa_handler = SIG_IGN;

После выполнения этого оператора нажатие клавиши прерывания будет безрезультатным. Снова разрешить прерывание можно так:

act.sa_handler = SIG_IGN;

sigaction (SIGINT, &act, NULL);

sigaction (SIGQUIT, &act, NULL);

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

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

#include <signal.h>

static struct sigaction act, oact;

/*Сохранить старый обработчик сигнала SIGTERM*/

sigaction (SIGTERM, NULL, &oact);

/*Определить новый обработчик сигнала SIGTERM*/

act.sa_handler = SIG_IGN;

sigaction (SIGTERM, &act, NULL);

/*Выполнить какие-либо действия*/

/*Восстановить старый обработчик*/

sigaction (SIGTERM, &oact, NULL);

Предположим, что программа использует временный рабочий файл. Следующая простая процедура удаляет файл:

/*Аккуратный выход из программы*/

#include <stdio.h>

#include <stdlib.h>

void g_exit (int s)

{

unlink (“tempfile”);

fprintf (stderr, “Прерывание – выход из программы&bsol;n”);

exit (1);

}

Можно связать эту процедуру с определенным сигналом:

extern void g_exit (int);

...

static struct sigaction act;

act.sa_handler = g_exit;

sigaction (SIGINT, &act, NULL);

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