Второе поле sa_mask
демонстрирует первое применение набора сигналов: сигналы, установленные в sa_mask
, будут блокироваться на время выполнения обработчика sa_handler
(при вызове sa_handler
и сам сигнал signo
будет неявно добавлен в набор sa_mask
, поэтому его можно не указывать явно). Это не значит, что поступившие в это время сигналы будут игнорироваться и теряться, просто их обработка будет отложена до завершения работы обработчика sa_handler
.[29]
Поле sa_flags
может использоваться для изменения характера реакции на сигнал signo
. Возможны следующие значения поля флагов:
• SA_RESETHAND
— после выполнения функции обработчика будет восстановлен обработчик по умолчанию (SIG_DFL
, что соответствует духу модели «ненадежных сигналов» и позволяет воспроизводить ее поведение);
• SA_NOCLDSTOP
— используется только для сигнала SIGCHLD
; флаг указывает системе не генерировать для родительского процесса SIGCHLD
от порожденных процессов, которые завершаются посредством SIGSTOP
.
• SA_SIGINFO
— при этом будет использована обработка сигналов на базе очереди сигналов (модель сигналов реального времени). По умолчанию используется простая обработка: результат воздействия нескольких сигналов определяется последним поступившим. В случае установки этого флага будет использована расширенная форма обработчика sa_sigaction
(при этом поле sa_handler
не будет использоваться)[30]. Обработчику будет передаваться дополнительная информация о сигнале — структура siginfo_t
(его номер, PID пославшего сигнал процесса, действующий идентификатор пользователя этого процесса). Эта весьма объемная структура будет очень кратко рассмотрена ниже.[31] Ее описание вынесено в отдельный заголовочный файл <sys/siginfo.h>
и может быть изучено там.
Приведем несколько небольших и самых простых примеров использования модели надежных сигналов.
1. Перехватчик сигнала SIGINT
(реакция на пользовательский ввод [Ctrl+C])[32] (
void catchint(int signo) {
cout << 'SIGINT: signo = ' << signo << endl;
}
int main() {
static struct sigaction act = { &catchint, 0, (sigset_t)0 };
// запрещаем любые сигналы на время обработки SIGINT:
sigfillset(&(act.sa_mask));
// до этого вызова реакцией на Ctrl+C будет завершение задачи:
sigaction(SIGINT, &act, NULL);
for (int i = 0; i < 20; i++)
sleep(1), cout << 'Cycle # ' << i << endl;
}
Результатом нормального (без вмешательства оператора) выполнения приложения будет последовательность из 20 циклов секундных ожиданий, но если в процессе этих ожиданий пользователь пытается прервать работу процесса по [Ctrl+C], то он получит вывод, подобный следующему:
...
Cycle # 10
... здесь пользователь пытается прервать программу
SIGINT: signo = 2
Cycle # 11
...
2. Запрет прерывания выполнения программы с терминала. Для этого достаточно заменить строку инициализации структуры sigaction
на:
static struct sigaction act = { SIG_IGN, 0, (sigset_t)0 };
Можно проигнорировать сразу несколько сигналов (прерывающих выполнение программы с клавиатуры):
sigaction(SIGINT, &act, NULL );
sigaction(SIGQUIT, &act, NULL);
Далее остановимся еще на одном вызове API-сигналов, который широко используется в этой и последующих моделях обработки (сигналы реального времени, реакция в потоках):
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
Этот вызов позволяет прочитать текущее значение (если set
установлено в NULL
, то параметр how
игнорируется) или переустановить сигнальную маску для текущего потока. Параметры вызова:
• set
— это то значение, в соответствии с которым корректируется сигнальная маска процесса;
• how
— указывает, какое именно действие переустановки сигнальной маски требуется осуществить:
• SIG_BLOCK
— добавить сигналы, указанные в set к маске процесса (заблокировать реакцию на эти сигналы);
• SIG_UNBLOCK
— сбросить указанные set сигналы в сигнальной маске;
• SIG_SETMASK
— переустановить сигнальную маску процесса на значение, указанное в set
.
• oset
— значение, в котором будет сохранено значение маски, предшествующее вызову (старое значение).
Как и большинство сигнальных функций, данная функция возвращает нулевое значение в результате успешного выполнения и -1 в случае неудачи, при этом код ошибки устанавливается в errno
.
Именно эта функция снимает одно из самых существенных ограничений, свойственных модели «ненадежных сигналов», — позволяет заблокировать реакцию на сигналы при выполнении критических участков кода и восстановить ее при завершении выполнения этих участков.
Модель сигналов реального времени
Сигналы реального времени были добавлены в POSIX относительно недавно (1996 г.). Эта новая модель в различных ОС UNIX реализуется с разной степенью полноты и с отклонениями от спецификаций, и QNX не исключение. Модель еще до конца не отработана, поэтому возможны сюрпризы (и сейчас они будут).
Модель сигналов реального времени, которую специфицирует POSIX, устанавливается флагом SA_SIGINFO
(который уже упоминался выше) при вызове sigaction()
. В нижеследующем перечислении того, что предусматривает эта модель, мы излагаем в первую очередь качественную картину происходящего, предлагаемую POSIX, кое-где уточняя ее конкретными данными реализации QNX (артефакты в поведении QNX будут отдельно отмечены позже):
1. Сигналы, называемые сигналами реального времени, могут принимать значения между