Сегодня поговорим про вызов, который активно рекомендуется к использованию в качестве альтернативы примитивному signal(). Несмотря на то, что sigaction() сложнее в использовании, взамен он предоставляет большую гибкость:
#include <signal.h>
// Возвращает 0 при успешном завершении, –1 при ошибке
int sigaction(int sig, const struct sigaction *act, struct sigaction *oldact);
Аргумент sig означает сигнал, который мы хотим перехватить. Значением может быть любой экземпляр, кроме SIGKILL или SIGSTOP.
Аргумент act — указатель на структуру, определяющую условия перехвата. Если нам необходимо получить актуальное значения, без изменений, можно указать NULL.
Аргумент oldact — это указатель на структуру такого же типа. Используется для возврата информации о предыдущих правилах перехвата. Если нам это неинтересно, можно также задать значение NULL.
Структура, на которую указывают аргументы act и oldact, выглядит следующим образом:
struct sigaction
{
/* Адрес обработчика */
void (*sa_handler)(int);
/* Адрес расширенного обработчика */
void (*sa_sigaction)(int, siginfo_t *, void *);
/* Сигналы, блокируемые во время вызова обработчика */
sigset_t sa_mask;
/* Флаги, контролирующие активацию обработчика */
int sa_flags;
/* Устарел и не должен использоваться */
void (*sa_restorer)(void);
};
Поле sa_handler соотносится с аргументом handler, передаваемым функции signal(). В данном поле указывается либо адрес обработчика либо одна из констант — SIG_IGN (сигнал будет проигнорирован) или SIG_DFL (процесс ответит дефолтным поведением на сигнал).
Поле sa_mask определяет список сигналов, блокируемых во время активации обработчика. После старта его выполнения, все сигналы из набора автоматически добавляются в сигнальную маску. После того, как произойдет возврат из обработчика, они автоматически удалятся.
Сделано это для того, чтобы никакой из сигналов не прервал выполнение обработчика.
Поле sa_flags — битовая маска, устанавливающая разные параметры, контролирующие обработку сигнала. Биты могут быть объединены в этом поле битовой операцией ИЛИ (|). Более детально ознакомиться с флагами можете в мануале.
Простой пример использования вызова sigaction():
#include <signal.h>
#include <stdio.h>
void handle_signal(int sig) {
printf("Caught signal %d\n", sig);
}
int main() {
struct sigaction sa;
sa.sa_handler = handle_signal;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
if (sigaction(SIGINT, &sa, NULL) == -1) {
perror("sigaction");
return 1;
}
while (1);
}
И да, как видите, я проинициализировал почти все поля структуры. Если вы работаете на C, не забывайте, что это не C++ и тут нет конструкторов, деструкторов, дефолтной инициализации. Если явно не указать значения переменным, они могут спокойно заполниться мусором, что приведет к UB (undefined behavior).
Где sigaction() прям 100% полезен?
Мы с вами еще не говорили подробно о сигналах реального времени, но при работе с ними вам прям станет ясна необходимость sigaction(). Дело в том, что через настройку перехватчика мы cможем принимать дополнительные аргументы - пользовательские данные. Использовать для этого нам нужно флаг SA_SIGINFO:
sa.sa_flags = SA_SIGINFO;
Если флаг указан, обработчик должен присваиваться полю sa_sigaction и принимать расширенный интерфейс для пользовательских данных:
void handler(int sig, siginfo_t *info, void *context);
sa.sa_sigaction = handler;
LinuxCamp