Категории
Самые читаемые
PochitayKnigi » Компьютеры и Интернет » Программное обеспечение » Linux программирование в примерах - Арнольд Роббинс

Linux программирование в примерах - Арнольд Роббинс

Читать онлайн Linux программирование в примерах - Арнольд Роббинс

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 87 88 89 90 91 92 93 94 95 ... 165
Перейти на страницу:

10.6.1. Обнажение проблемы

Что неладно с API System V Release 3? В конце концов, они предоставляют блокирование сигналов, так, что сигналы не теряются, и любой данный сигнал может быть надежно обработан.

Ответ в том, что этот API работает лишь с одним сигналом в одно и то же время. Программы обычно обрабатывают больше одного сигнала. И когда вы находитесь в середине процесса обработки одного сигнала, вы не хотите беспокоиться по поводу обработки еще и второго. (Предположим, вы только что начали отвечать по офисному телефону, когда зазвонил ваш мобильный телефон: вы бы предпочли, чтобы телефонная система ответила вызывающему, что в данный момент вы находитесь на другой линии и что скоро освободитесь, вместо того, чтобы проделывать все это самому.)

С API sigset() каждый обработчик сигнала должен был бы временно блокировать все другие сигналы, сделать свою работу, а затем разблокировать их. Проблема в том, что в промежутке между любыми двумя вызовами sighold() может появиться еще не заблокированный сигнал. Сценарий, еще раз, распространенный, создающий условия гонки.

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

10.6.2. Наборы сигналов: sigset_t и связанные функции

Маска сигналов процесса является списком сигналов, которые процесс в настоящее время заблокировал. Сила POSIX API в том, что маской сигналов процесса можно манипулировать атомарно, как единым целым.

Маска сигналов процесса программно представляется с помощью набора сигналов. Это тип sigset_t. Концептуально он представляет собой просто битовую маску, причем значения 0 и 1 представляют отсутствие или наличие определенного сигнала в маске.

/* Непосредственное манипулирование маской сигналов. НЕ ДЕЛАЙТЕ ЭТОГО! */

int mask = (1 << SIGHUP) | (1 << SIGINT);

 /* битовая маска для SIGHUP и SIGINT */

Однако, поскольку в системе может быть больше сигналов, чем может содержаться в одной int или long и поскольку интенсивное использование побитовых операций тяжело для восприятия, для управления наборами сигналов существует несколько функций API.

#include <signal.h> /* POSIX */

int sigemptyset(sigset_t *set);

int sigfillset(sigset_t *set);

int sigaddset(sigset_t *set, int signum);

int sigdelset(sigset_t *set, int signum);

int sigismember(const sigset_t *set, int signum);

Эти функции следующие:

int sigemptyset(sigset_t *set)

Освобождает набор сигналов. По возвращении *set не содержит сигналов. Возвращает 0 в случае успеха и -1 при ошибке.

int sigfillset(sigset_t *set)

Полностью заполняет набор сигналов. По возвращении *set содержит все сигналы, определенные системой. Возвращает 0 в случае успеха и -1 при ошибке.

int sigaddset(sigset_t *set, int signum)

Добавляет signum к маске сигналов процесса в *set. Возвращает 0 в случае успеха и -1 при ошибке.

int sigdelset(sigset_t *set, int signum)

Удаляет signum из маски сигналов процесса в *set. Возвращает 0 в случае успеха и -1 при ошибке.

int sigismember(const sigset_t *set, int signum)

Возвращает true/false, если signum присутствует или не присутствует в *set.

Перед выполнением с переменной sigset_t каких-то действий всегда следует вызывать одну из функций sigemptyset() или sigfillset(). Существуют оба интерфейса, поскольку иногда бывает нужно начать с пустого набора и работать потом лишь с одним или двумя сигналами, а в другое время бывает нужно работать со всеми сигналами, возможно, убирая один или два сигнала.

10.6.3. Управление маской сигналов: sigprocmask() и др.

Маска сигналов процесса вначале пуста - заблокированных сигналов нет. (Это упрощение; см. раздел 10.9 «Сигналы, передающиеся через fork() и exec().) Три функции позволяют работать непосредственно с маской сигналов процесса:

#include <signal.h> /* POSIX */

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

int sigpending(sigset_t *set);

int sigsuspend(const sigset_t *set);

Функции следующие:

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset)

Если oldset не равен NULL, получается маска сигналов текущего процесса и помещается в *oldset. Затем маска сигналов процесса обновляется в соответствии с содержимым set и значением how, который должен иметь одно из следующих значений:

SIG_BLOCK  Объединить сигналы в *set с маской сигналов текущего процесса. Новая маска является объединением текущей маски и *set.

SIG_UNBLOCK  Удалить сигналы в *set из маски сигналов процесса. Это не представляет проблемы, если *set содержит сигнал, который не содержится в текущей маске сигналов процесса.

SIG_SETMASK  Заменить маску сигналов процесса содержимым *set.

Если set равен NULL, a oldset — нет, значение how неважно. Эта комбинация получает маску сигналов текущего процесса, не меняя ее. (Это явно выражено в стандарте POSIX, но не ясно из справочной страницы GNU/Linux.)

int sigpending(sigset_t *set)

Эта функция позволяет увидеть, какие сигналы ожидают решения; т.е. *set заполнен этими сигналами, которые были посланы, но они еще не доставлены, поскольку заблокированы.

int sigsuspend(const sigset_t *set)

Эта функция временно заменяет маску сигналов процесса содержимым *set, а затем приостанавливает процесс, пока сигнал не будет получен. По определению, заставить функцию вернуться может только сигнал, не находящийся в *set (см. раздел 10.7 «Сигналы для межпроцессного взаимодействия).

10.6.4. Перехват сигналов: sigaction()

Наконец мы готовы взглянуть на функцию sigaction(). Эта функция сложна, и мы намеренно опускаем множество деталей, которые предназначены для специального использования. Стандарт POSIX и справочная страница sigaction(2) предоставляют все подробности, хотя вы должны тщательно прочесть и то, и другое, чтобы полностью все усвоить.

#include <signal.h> /* POSIX */

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

Аргументы следующие:

int signum

Интересующий сигнал, как в случае с другими функциями обработки сигналов.

const struct sigaction *act

Определение нового обработчика для сигнала signum.

struct sigaction *oldact

Определение текущего обработчика. Если не NULL, система до установки *act заполняет *oldact. *act может быть NULL, в этом случае *oldact заполняется, но больше ничего не меняется.

Таким образом, sigaction() и устанавливает новый обработчик, и получает старый за одно действие. struct sigaction выглядит следующим образом.

/* ПРИМЕЧАНИЕ: Порядок в структуре может варьировать. Могут быть

   также и другие поля! */

struct sigaction {

 sigset_t sa_mask; /* Дополнительные сигналы для блокирования */

 int sa_flags;     /* Контролирует поведение */

 void (*sa_handler)(int);

  /* Может образовать объединение с sa_sigaction */

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

  /* Может образовать объединение с sa_handler */

}

Поля следующие:

sigset_t sa_mask

Набор дополнительных сигналов для блокирования при запуске функции обработчика. Таким образом, когда вызывается обработчик, общий набор заблокированных сигналов является объединением сигналов в маске процесса, сигналов в act->mask и, если SA_NODEFER сброшен, signum.

int sa_flags

Флаги, контролирующие обработку сигнала ядром. См. обсуждение далее.

void (*sa_handler)(int)

Указатель на «традиционную» функцию обработчика. У нее такой же прототип (возвращаемый тип и список параметров), как у функции обработчика для signal(), bsd_signal() и sigset().

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

Указатель на функцию обработчика «нового стиля». Функция принимает три аргумента, которые вскоре будут описаны.

Которая из функций act->sa_handler и act->sa_sigaction используется, зависит от флага SA_SIGINFO в act->sa_flags. Когда имеется, используется act->sa_sigaction; в противном случае используется act->sa_handler. Как POSIX, так и справочная страница GNU/Linux указывают, что эти два поля могут перекрываться в памяти (т. е. быть частью union). Таким образом, никогда не следует использовать оба поля в одной и той же struct sigaction.

Поле sa_flags составляется с помощью побитового ИЛИ значений одного или более флагов, перечисленных в табл. 10.3.

1 ... 87 88 89 90 91 92 93 94 95 ... 165
Перейти на страницу:
Тут вы можете бесплатно читать книгу Linux программирование в примерах - Арнольд Роббинс.
Комментарии