В особенности полезно значение SI_USER; оно позволяет обработчику сигнала сообщить, был ли сигнал послан функциями raise() или kill() (описываются далее). Вы можете использовать эту информацию, чтобы избежать повторного вызова raise() или kill().
Третий аргумент обработчика сигнала с тремя аргументами, void *context, является расширенной возможностью, которая больше не обсуждается в данной книге.
Наконец, чтобы увидеть sigaction() в действии, исследуйте полный исходный код обработчика сигнала для sort.c:
2074 static void
2075 sighandler(int sig)
2076 {
2077 #ifndef SA_NOCLDSTOP /* В системе старого стиля... */
2078 signal(sig, SIG_IGN); /* - для игнорирования sig используйте signal()*/
2079 #endif - /* В противном случае sig автоматически блокируется */
2080
2081 cleanup(); /* Запуск кода очистки */
2082
2083 #ifdef SA_NOCLDSTOP /* В системе в стиле POSIX... */
2084 {
2085 struct sigaction sigact;
2086
2087 sigact.sa_handler = SIG_DFL; /* - Установить действие по умолчанию */
2088 sigemptyset(&sigact.sa_mask); /* - Нет дополнительных сигналов для блокирования */
2089 sigact.sa_flags = 0; /* - Специальные действия не предпринимаются */
2090 sigaction(sig, &sigact, NULL); /* - Поместить на место */
2091 }
2092 #else /* На системе в старом стиле... */
2093 signal(sig, SIG_DFL); /* - Установить действие по умолчанию */
2094 #endif
2095
2096 raise(sig); /* Повторно послать сигнал */
2097 }
Вот код в main(), который помещает обработчик на свое место:
2214 #ifdef SA_NOCLDSTOP /* На системе POSIX... */
2215 {
2216 unsigned i;
2217 sigemptyset(&caught_signals);
2218 for (i = 0; i < nsigs; i++) /* - Блокировать все сигналы */
2219 sigaddset(&caught_signals, sigs[i]);
2220 newact.sa_handler = sighandler; /* - Функция обработки сигнала */
2221 newact.sa_mask = caught_signals; /* - Установить для обработчика маску сигналов процесса */
2222 newact.sa_flags =0; /* - Особых флагов нет */
2223 }
2224 #endif
2225
2226 {
2227 unsigned i;
2228 for (i = 0; i < nsigs; i++) /* Для всех сигналов... */
2229 {
2230 int sig = sigs[i];
2231 #ifdef SA_NOCLDSTOP
2232 sigaction(sig, NULL, &oldact); /* - Получить старый обработчик */
2233 if (oldact.sa_handler != SIG_IGN) /* - Если этот сигнал не игнорируется */
2234 sigaction(sig, &newact, NULL); /* - Установить наш обработчик */
2235 #else
2236 if (signal(sig, SIG_IGN) != SIG_IGN)
2237 signal(sig, sighandler); /* - Та же логика со старым API */
2238 #endif
2239 }
2240 }
Мы заметили, что строки 2216–2219 и 2221 могут быть замещены одним вызовом: sigfillset(&newact.sa_mask);
Мы не знаем, почему код написан именно таким способом.
Интерес представляют также строки 2233–2234 и 2236–2237, которые показывают правильный способ проверки того, игнорируется ли сигнал, и для установки обработчика лишь в том случае, если сигнал не игнорируется.
ЗАМЕЧАНИЕ. Функции API sigaction() и signal() не должны использоваться вместе для одного и того же сигнала. Хотя POSIX идет на большие длинноты, чтобы сначала сделать возможным использование signal(), получить struct sigaction, представляющую диспозицию signal(), и восстановить ее, все равно это плохая мысль. Код будет гораздо проще читать, писать и понимать, если вы используете одну функцию или другую взаимоисключающим образам
10.6.5. Извлечение ожидающих сигналов: sigpending()
Описанный ранее системный вызов sigpending() позволяет получить набор ожидающих сигналов, т.е тех сигналов, которые появились, но еще не доставлены из-за блокировки:
#include <signal.h> /* POSIX */
int sigpending(sigset_t *set);
Помимо разблокировки ожидающих сигналов, чтобы они могли быть доставлены, вы можете решить их игнорировать. Установка действия сигнала SIG_IGN вызывает сбрасывание сигнала (даже если он был заблокирован). Сходным образом для тех сигналов, действием по умолчанию для которых является их игнорирование, установка действия в SIG_DFL также вызывает сбрасывание таких ожидающих сигналов.
10.6.6. Создание возможности для прерывания функций: siginterrupt()
Чтобы сделать определенную функцию прерываемой или повторно запускаемой в зависимости от значения второго аргумента, в качестве удобного средства может использоваться функция siginterrupt(). Объявление следующее:
#include <signal.h> /* XSI */
int siginterrupt(int sig, int flag);
В соответствии со стандартом POSIX поведение siginterrupt() эквивалентно следующему коду:
int siginterrupt(int sig, int flag) {
int ret;
struct sigaction act;
(void)sigaction(sig, NULL, &act); /* Получить старые установки */
if (flag) /* Если flag равен true... */
act.sa_flags &= ~SA_RESTART; /* Запретить повторный запуск */
else /* В противном случае... */
act.sa_flags |= SA_RESTART; /* Разрешить повторный запуск */
ret = sigaction(sig, &act, NULL);
/* Поместить новые установки на место */
return ret; /* Вернуть результат */
}
В случае успеха возвращаемое значение равно 0 и -1 при ошибке.