Используйте, sigaction()
если у вас нет веских причин не делать этого.
У signal()
интерфейса есть древность (и, следовательно, доступность) в свою пользу, и он определен в стандарте C. Тем не менее, у него есть ряд нежелательных характеристик, которых можно sigaction()
избежать - если вы не используете явно добавленные флаги, чтобы sigaction()
позволить ему точно имитировать старое signal()
поведение.
signal()
Функция не (обязательно) блокировать другие сигналы от прибывают в то время как текущий обработчик выполнения; sigaction()
может блокировать другие сигналы, пока не вернется текущий обработчик.
signal()
Функция (обычно) сбрасывает обратное действие сигнала к SIG_DFL
( по умолчанию) для почти всех сигналов. Это означает, что signal()
обработчик должен переустановить себя в качестве своего первого действия. Он также открывает окно уязвимости между моментом обнаружения сигнала и переустановкой обработчика, во время которого, если приходит второй экземпляр сигнала, происходит поведение по умолчанию (обычно прекращается, иногда с предубеждением - также известный как дамп ядра).
- Точное поведение
signal()
зависит от системы - и стандарты допускают такие вариации.
Обычно это веские причины для использования sigaction()
вместо signal()
. Однако интерфейс, sigaction()
несомненно, более неудобный.
Какой из двух вы используете, не поддавайтесь искушению альтернативными интерфейсами сигналов , таких как
sighold()
,
sigignore()
,
sigpause()
и
sigrelse()
. Они номинально являются альтернативой sigaction()
, но они едва стандартизированы и присутствуют в POSIX для обратной совместимости, а не для серьезного использования. Обратите внимание, что в стандарте POSIX указано, что их поведение в многопоточных программах не определено.
Многопоточные программы и сигналы - совсем другая сложная история. AFAIK, оба signal()
и sigaction()
в порядке в многопоточных приложениях.
Cornstalks замечает :
На странице signal()
руководства Linux говорится:
Эффекты signal()
в многопоточном процессе не определены.
Таким образом, я думаю, sigaction()
это единственное, что можно безопасно использовать в многопоточном процессе.
Это интересно. В этом случае страница руководства Linux более строгая, чем POSIX. POSIX определяет signal()
:
Если процесс является многопоточным или если процесс является однопоточным и обработчик сигналов выполняется иначе, чем в результате:
- Процесс вызова
abort()
, raise()
, kill()
, pthread_kill()
, или , sigqueue()
чтобы генерировать сигнал , который не блокирован
- Ожидающий сигнал разблокируется и доставляется до вызова, который его разблокировал.
поведение не определено, если обработчик сигнала обращается к любому объекту, отличному от errno
статического продолжительности хранения, кроме присвоения значения объекту, объявленному как volatile sig_atomic_t
, или если обработчик сигнала вызывает любую функцию, определенную в этом стандарте, кроме одной из функций, перечисленных в Сигнальные концепции .
Итак, POSIX четко определяет поведение signal()
в многопоточном приложении.
Тем не менее, sigaction()
он должен быть предпочтительным практически во всех обстоятельствах - и переносимый многопоточный код следует использовать, sigaction()
если нет веской причины, по которой он не может (например, «использовать только функции, определенные стандартом C» - и да, код C11 может быть многопоточным -резьбовой). Об этом, по сути, и говорится в первом абзаце этого ответа.
signal
фактически описывает поведение Unix System V. POSIX допускает либо такое поведение, либо гораздо более разумное поведение BSD, но, поскольку вы не можете быть уверены, какой из них вы получите, лучше использовать егоsigaction
.SA_RESETHAND
, тожеSA_NODEFER
.sigaction()
, то вы по существу обязаны использовать спецификацию Standard C дляsignal()
. Однако это дает вам чрезвычайно ограниченный набор вариантов того, что вы можете делать. Вы можете: изменять (область файла) переменные типаvolatile sig_atomic_t
; вызвать одну из функций «быстрого выхода» (_Exit()
,quick_exit()
) илиabort()
; вызовsignal()
с текущим номером сигнала в качестве аргумента сигнала; возвращение. Вот и все. Переносимость всего остального не гарантируется. Это настолько строго, что большинство людей игнорируют эти правила, но полученный в результате код выглядит хитроумным.sigaction()
демонстрация от самого GCC: gnu.org/software/libc/manual/html_node/… ; и отличнаяsignal()
демонстрация от самого GCC: gnu.org/software/libc/manual/html_node/… . Обратите внимание, что вsignal
демонстрации они избегают изменения обработчика ignore (SIG_IGN
), если это было намеренно установлено ранее.Для меня этой строки было достаточно, чтобы решить:
http://pubs.opengroup.org/onlinepubs/009695399/functions/signal.html#tag_03_690_07
Независимо от того, начинаете ли вы с нуля или изменяете старую программу, sigaction должен быть правильным вариантом.
источник
Это разные интерфейсы для сигнальных устройств ОС. Следует предпочесть использование sigaction для сигнализации, если это возможно, поскольку signal () имеет поведение, определяемое реализацией (часто склонное к гонке), и ведет себя по-разному в Windows, OS X, Linux и других системах UNIX.
Подробнее см. Это примечание по безопасности .
источник
signal () - это стандартный C, sigaction () - нет.
Если вы можете использовать любой из них (то есть вы работаете в системе POSIX), используйте sigaction (); не указано, сбрасывает ли signal () обработчик, а это означает, что для переносимости вы должны снова вызвать signal () внутри обработчика. Что еще хуже, есть гонка: если вы получите два сигнала в быстрой последовательности, а второй будет доставлен до того, как вы переустановите обработчик, у вас будет действие по умолчанию, которое, вероятно, приведет к уничтожению вашего процесса. sigaction () , с другой стороны, гарантированно использует «надежную» семантику сигнала. Вам не нужно переустанавливать обработчик, потому что он никогда не будет сброшен. С помощью SA_RESTART вы также можете получить некоторые системные вызовы для автоматического перезапуска (так что вам не нужно вручную проверять EINTR). sigaction () имеет больше возможностей и надежен, поэтому его использование приветствуется.
Psst ... не говорите никому, что я вам это сказал, но в POSIX в настоящее время есть функция bsd_signal (), которая действует как signal (), но дает семантику BSD, что означает ее надежность. Его основное использование - перенос старых приложений, которые предполагали надежные сигналы, и POSIX не рекомендует его использовать.
источник
bsd_signal()
- некоторые реализации POSIX могут иметь эту функцию, но сам POSIX не содержит такой функции (см. POSIX ).Короче говоря:
sigaction()
это хорошо и четко определено, но это функция Linux, поэтому она работает только в Linux.signal()
плохая и плохо определенная, но это стандартная функция C, поэтому она работает с чем угодно.Что об этом говорится на страницах руководства Linux?
man 2 signal
(см. здесь онлайн ) гласит:Другими словами: не используйте
signal()
. Используйтеsigaction()
вместо этого!Что думает GCC?
Источник: https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling
Итак, если и Linux, и GCC говорят не использовать
signal()
, а использоватьsigaction()
вместо этого, возникает вопрос: как, черт возьми, мы используем эту запутаннуюsigaction()
вещь !?Примеры использования:
Прочтите
signal()
здесь ОТЛИЧНЫЙ пример GCC : https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-HandlingИ их ОТЛИЧНЫЙ
sigaction()
пример здесь: https://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.htmlПрочитав эти страницы, я придумал следующую технику
sigaction()
:1.
sigaction()
, так как это правильный способ прикрепить обработчик сигнала, как описано выше:#include <errno.h> // errno #include <signal.h> // sigaction() #include <stdio.h> // printf() #include <string.h> // strerror() #define LOG_LOCATION __FILE__, __LINE__, __func__ // Format: const char *, unsigned int, const char * #define LOG_FORMAT_STR "file: %s, line: %u, func: %s: " /// @brief Callback function to handle termination signals, such as Ctrl + C /// @param[in] signal Signal number of the signal being handled by this callback function /// @return None static void termination_handler(const int signal) { switch (signal) { case SIGINT: printf("\nSIGINT (%i) (Ctrl + C) signal caught.\n", signal); break; case SIGTERM: printf("\nSIGTERM (%i) (default `kill` or `killall`) signal caught.\n", signal); break; case SIGHUP: printf("\nSIGHUP (%i) (\"hang-up\") signal caught.\n", signal); break; default: printf("\nUnk signal (%i) caught.\n", signal); break; } // DO PROGRAM CLEANUP HERE, such as freeing memory, closing files, etc. exit(signal); } /// @brief Set a new signal handler action for a given signal /// @details Only update the signals with our custom handler if they are NOT set to "signal ignore" (`SIG_IGN`), /// which means they are currently intentionally ignored. GCC recommends this "because non-job-control /// shells often ignore certain signals when starting children, and it is important for children /// to respect this." See /// https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling /// and https://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.html. /// Note that termination signals can be found here: /// https://www.gnu.org/software/libc/manual/html_node/Termination-Signals.html#Termination-Signals /// @param[in] signal Signal to set to this action /// @param[in] action Pointer to sigaction struct, including the callback function inside it, to attach to this signal /// @return None static inline void set_sigaction(int signal, const struct sigaction *action) { struct sigaction old_action; // check current signal handler action to see if it's set to SIGNAL IGNORE sigaction(signal, NULL, &old_action); if (old_action.sa_handler != SIG_IGN) { // set new signal handler action to what we want int ret_code = sigaction(signal, action, NULL); if (ret_code == -1) { printf(LOG_FORMAT_STR "sigaction failed when setting signal to %i;\n" " errno = %i: %s\n", LOG_LOCATION, signal, errno, strerror(errno)); } } } int main(int argc, char *argv[]) { //... // Register callbacks to handle kill signals; prefer the Linux function `sigaction()` over the C function // `signal()`: "It is better to use sigaction if it is available since the results are much more reliable." // Source: https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling // and /programming/231912/what-is-the-difference-between-sigaction-and-signal/232711#232711. // See here for official gcc `sigaction()` demo, which this code is modeled after: // https://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.html // Set up the structure to specify the new action, per GCC's demo. struct sigaction new_action; new_action.sa_handler = termination_handler; // set callback function sigemptyset(&new_action.sa_mask); new_action.sa_flags = 0; // SIGINT: ie: Ctrl + C kill signal set_sigaction(SIGINT, &new_action); // SIGTERM: termination signal--the default generated by `kill` and `killall` set_sigaction(SIGTERM, &new_action); // SIGHUP: "hang-up" signal due to lost connection set_sigaction(SIGHUP, &new_action); //... }
2. И
signal()
хотя это не лучший способ прикрепить обработчик сигнала, как описано выше, все же хорошо знать, как его использовать.Вот скопированный демонстрационный код GCC, и он настолько хорош, насколько это возможно:
#include <signal.h> void termination_handler (int signum) { struct temp_file *p; for (p = temp_file_list; p; p = p->next) unlink (p->name); } int main (void) { … if (signal (SIGINT, termination_handler) == SIG_IGN) signal (SIGINT, SIG_IGN); if (signal (SIGHUP, termination_handler) == SIG_IGN) signal (SIGHUP, SIG_IGN); if (signal (SIGTERM, termination_handler) == SIG_IGN) signal (SIGTERM, SIG_IGN); … }
Основные ссылки, о которых следует знать:
signal()
пример использования GCC : https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handlingsigaction()
пример использования GCC : https://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.htmlsigemptyset()
иsigfillset()
; Я до сих пор не совсем понимаю это, но знаю, что они важны: https://www.gnu.org/software/libc/manual/html_node/Signal-Sets.htmlСмотрите также:
источник
На
signal(3)
странице руководства :Оба вызывают одно и то же основное средство. Вы, по-видимому, не должны манипулировать ответом на один сигнал обоими, но их смешивание не должно приводить к поломке ...
источник
Я бы также предложил использовать sigaction () вместо signal () и хотел бы добавить еще один момент. sigaction () предоставляет дополнительные параметры, такие как pid умершего процесса (возможно с использованием структуры siginfo_t).
источник
Я бы использовал signal (), поскольку он более переносимый, по крайней мере теоретически. Я проголосую за любого комментатора, который сможет предложить современную систему, не имеющую уровня совместимости с POSIX и поддерживающую signal ().
Цитата из документации GLIBC :
источник
Из справочной страницы signal (7)
И я бы сказал, что эта «проблема» существует для signal (2) и sigaction (2) . Так что будьте осторожны с сигналами и потоками.
... и signal (2), кажется, вызывает sigaction (2) в Linux с помощью glibc.
источник