Linux программирование в примерах - Арнольд Роббинс
Шрифт:
Интервал:
Закладка:
Поле sa_flags составляется с помощью побитового ИЛИ значений одного или более флагов, перечисленных в табл. 10.3.
Таблица 10.3. Значения флагов для sa_flags
Флаг Значение SA_NOCLDSTOP Этот флаг имеет смысл лишь для SIGCHLD. Когда он установлен, родитель не получает сигнал при остановке порожденною процесса сигналами SIGSTOP, SIGTSTP, SIGTTIN или SIGTTOU. Эти сигналы обсуждаются позже, в разделе 10.8.2 SA_NOCLDWAIТ Этот флаг имеет смысл лишь для SIGCHLD. Его поведение сложно. Мы отложим объяснение на потом, см. раздел 10.8.3 SA_NODEFER Обычно данный сигнал блокируется, когда вызывается обработчик сигнала. Когда установлен один из этих флагов, данный сигнал не блокируется при запуске обработчика SA_NODEFER является официальным именем POSIX данного флага (которое и следует использовать) SA_NOMASK Альтернативное имя для SA_NODEFER[110] SA_SIGINFO Обработчик сигнала принимает три аргумента. Как упоминалось, при данном установленном флаге должно использоваться поле sa_sigaction вместо sa_handler. SA_ONSTACK Это продвинутая возможность. Обработчики сигналов могут вызываться с использованием предоставленной пользователем памяти в качестве «альтернативного стека сигнала». Эта память даётся ядру для подобного использования посредством sigaltstack() (см. sigaltstack(2)). Эта особенность больше не описывается в данной книге SA_RESETHAND Этот флаг обеспечивает поведение V7: после вызова обработчика восстанавливается действие сигнала по умолчанию. Официальным именем POSIX флага (которое следует использовать) является SA_RESETHAND SA_ONESHOT Альтернативное имя для SA_RESETHAND. SA_RESTART Этот флаг предоставляет семантику BSD: системные вызовы, которые могут завершиться с ошибкой EINTR и которые получают этот сигнал, запускаются повторно.Когда в act->sa_flags установлен флаг SA_SIGINFO, поле act->sa_sigaction является указателем на функцию, объявленную следующим образом:
void action_handler(int sig, siginfo_t *info, void *context) {
/* Здесь тело обработчика */
}
Структура siginfo_t предоставляет изобилие сведений о сигнале:
/* Определение POSIX 2001. Действительное содержание может на разных системах быть разным. */
typedef struct {
int si_signo; /* номер сигнала */
int si_errno; /* значение <errno.h> при ошибке */
int si_code; /* код сигнала; см. текст */
pid_t si_pid; /* ID процесса, пославшего сигнал */
uid_t si_uid; /* настоящий UID посылающего процесса */
void *si_addr; /* адрес вызвавшей ошибку инструкции */
int si_status; /* значение завершения, может включать death-by-signal */
long si_band; /* связывающее событие для SIGPOLL/SIGIO */
union sigval si_value; /* значение сигнала (расширенное) */
} siginfo_t;
Поля si_signo, si_code и si_value доступны для всех сигналов. Другие поля могут быть членами объединения, поэтому должны использоваться лишь для тех сигналов, для которых они определены. В структуре siginfo_t могут быть также и другие поля.
Почти все поля предназначены для расширенного использования. Все подробности содержатся в стандарте POSIX и справочной странице sigaction(2). Однако, мы можем описать простое использование поля si_code.
Для SIGBUS, SIGCHLD, SIGFPE, SIGILL, SIGPOLL, SIGSEGV и SIGTRAP поле si_code может принимать любое из набора предопределенных значений, специфичных для каждого сигнала, указывая на причину появления сигнала. Откровенно говоря, детали несколько чрезмерны; повседневному коду на самом деле нет необходимости иметь с ними дела (хотя позже мы рассмотрим значения для SIGCHLD). Для всех остальных сигналов член si_code имеет одно из значений из табл. 10.4.
Таблица 10.4. Значения происхождения сигнала для si_code
Значение Только GLIBC Смысл SI_ASYNCIO Асинхронный ввод/вывод завершен (расширенный). SI_KERNEL √ Сигнал послан ядром. SI_MESGQ Состояние очереди сообщений изменилось (расширенный.) SI_QUEUE Сигнал послан из sigqueue() (расширенный). SI_SIGIO √ SIGIO поставлен в очередь (расширенный). SI_TIMER Время таймера истекло SI_USER Сигнал послан функцией kill(). raise() и abort() также могут его вызвать, но это не обязательно.В особенности полезно значение 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);
Мы не знаем, почему код написан именно таким способом.