10.6.4. Перехват сигналов: sigaction()
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.
Таблица 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);
Мы не знаем, почему код написан именно таким способом.
Интерес представляют также строки 2233–2234 и 2236–2237, которые показывают правильный способ проверки того, игнорируется ли сигнал, и для установки обработчика лишь в том случае, если сигнал не игнорируется.
ЗАМЕЧАНИЕ. Функции API sigaction() и signal() не должны использоваться вместе для одного и того же сигнала. Хотя POSIX идет на большие длинноты, чтобы сначала сделать возможным использование signal(), получить struct sigaction, представляющую диспозицию signal(), и восстановить ее, все равно это плохая мысль. Код будет гораздо проще читать, писать и понимать, если вы используете одну функцию или другую взаимоисключающим образам
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОКЧитайте также
10.2. Действия сигналов
10.2. Действия сигналов Каждый сигнал (вскоре мы представим полный список) имеет связанное с ним действие по умолчанию. POSIX обозначает это как диспозицию (disposition) сигнала. Это то действие, которое ядро осуществляет для процесса, когда поступает определенный сигнал. Действие
12.2.1. Посылка сигналов
12.2.1. Посылка сигналов Посылка сигналов от одного процесса другому обычно осуществляется с помощью системного вызова kill(). Этот системный вызов подробно обсуждался в главе 10. Вариантом kill() является tkill(), который не предназначен для прямого использования в программах.int
12.2.3. Перехват сигналов
12.2.3. Перехват сигналов Вместо использования функции signal() (чья семантика в процессе эволюции стала неправильной) POSIX-программы регистрируют обработчики сигналов с помощью sigaction().#include <signal.h>int sigaction(int signum, struct sigaction *act, struct sigaction *oact);Этот системный вызов устанавливает
12.2.6. Ожидание сигналов
12.2.6. Ожидание сигналов Когда программа построена преимущественно вокруг сигналов, часто необходимо, чтобы она ожидала появления какого-то сигнала, прежде чем продолжать работу. Системный вызов pause() предоставляет простую возможность для этого.#include <unistd.h>int
12.3.1. Описание сигналов
12.3.1. Описание сигналов Иногда приложения нуждаются в описании сигнала для отображения пользователю или помещения в журнал. Существуют три способа сделать это (см. главу 9). К сожалению, ни один из них не стандартизован.Самый старый метод предусматривает применение sys_siglist
7.2.1 Обработка сигналов
7.2.1 Обработка сигналов Ядро обрабатывает сигналы в контексте того процесса, который получает их, поэтому чтобы обработать сигналы, нужно запустить процесс. Существует три способа обработки сигналов: процесс завершается по получении сигнала, не обращает внимание на
10.7.1. Перехват соединения
10.7.1. Перехват соединения Вспомним полный цикл соединения с FTP-сервером и передачи файлов:1. Соединение с сервером.2. Авторизация.3. Запрос на скачивание файлов.4. Сервер открывает порт и сообщает его клиенту.5. Клиент подключается к указанному порту и получает или передает
14.5.4. Перехват соединения
14.5.4. Перехват соединения Атака на компьютеры с помощью перехвата соединения была первый раз применена еще несколько десятков лет назад, и до сих пор единственным эффективным методом борьбы с ней является использование шифрования пакетов.Когда компьютеры по протоколу TCP
23.3.4. Обработка сигналов
23.3.4. Обработка сигналов Перед тем, как перейти к следующему пункту, нужно еще раз рассмотреть функцию gtk_signal_connect(). Данной функции нужно передать четыре параметра:? GtkObject *object — объект, которому может быть послан сигнал;? const gchar *name — имя сигнала, например, «destroy»;? GtkSignalFunc func
Перехват сигналов
Перехват сигналов Вы можете использовать встроенную в bash программу trap для перехвата сигналов в своих программах. Это хороший способ изящно завершать работу программы. Например, если пользователь, когда ваша программа работает, нажмет CTRL-C — программе будет отправлен
14.1.2. Перехват вывода программы
14.1.2. Перехват вывода программы Простейший способ перехватить информацию, выведенную программой, — заключить команду в обратные кавычки, например:listing = `ls -l` # Одна строка будет содержать несколько строчек (lines).now = `date` # "Mon Mar 12 16:50:11 CST 2001"Обобщенный ограничитель %x
11.3. Перехват исключений
11.3. Перехват исключений В языке C++ исключения обрабатываются в предложениях catch. Когда какая-то инструкция внутри try-блока возбуждает исключение, то просматривается список последующих предложений catch в поисках такого, который может его обработать.Catch-обработчик состоит
11.3.4. Перехват всех исключений
11.3.4. Перехват всех исключений Иногда функции нужно выполнить определенное действие до того, как она завершит обработку исключения, даже несмотря на то, что обработать его она не может. К примеру, функция захватила некоторый ресурс, скажем открыла файл или выделила память
26.3.1. Перехват сигналов и выполнение действий
26.3.1. Перехват сигналов и выполнение действий А теперь создадим сценарий, выполняющий подсчет итераций до тех пор, пока пользователь не нажмет комбинацию клавиш [Ctrl+C] (сигнал 2). После этого сценарий отобразит сообщение, содержащее номер текущей итерации цикла.В этом
Глава 11 Перехват сеанса
Глава 11 Перехват сеанса В этой главе обсуждаются следующие темы: • Основные сведения о перехвате сеанса • Популярные инструментальные средства перехвата сеанса • Исследование атак типа MITM в зашифрованных соединениях · Резюме · Конспект · Часто задаваемые вопросы
Перехват
Перехват Одним из наиболее мощных средств в борьбе с преступлениями, диверсиями и мятежами является возможность перехватывать письменные и устные коммуникации. Именно эту привилегию ФБР отстаивает особенно рьяно.Перехват давно известен в американской истории. В 1624