Answers:
sigaction()
除非有非常令人信服的理由,否则请使用。
该signal()
接口有利于古代(因此具有可用性),并且在C标准中进行了定义。但是,它具有许多sigaction()
可以避免的不良特性-除非您使用显式添加的标志sigaction()
以使其忠实地模拟旧signal()
行为。
signal()
函数不会(有必要)阻止当前处理程序执行时其他信号的到达;sigaction()
可以阻塞其他信号,直到当前处理程序返回为止。signal()
功能(通常)会将SIG_DFL
几乎所有信号的信号操作重置为(默认)。这意味着signal()
处理程序必须首先重新安装自身。它还在检测到信号与重新安装处理程序之间打开了一个漏洞窗口,在此期间,如果信号的第二个实例到达,则会发生默认行为(通常会终止,有时会有偏见-aka核心转储)。signal()
系统之间变化的确切行为-标准允许这些变化。这些通常是使用sigaction()
代替的充分理由signal()
。然而,sigaction()
不可否认的是,的界面更为复杂。
你使用这两种中的哪,不要被诸如替代信号接口被诱惑
sighold()
,
sigignore()
,
sigpause()
和
sigrelse()
。它们名义上是的替代品sigaction()
,但它们只是勉强标准化,并且存在于POSIX中是为了向后兼容,而不是为了认真使用。请注意,POSIX标准说它们在多线程程序中的行为是不确定的。
多线程程序和信号完全是另一个复杂的故事。 据我所知,无论是signal()
和sigaction()
在多线程应用程序确定。
Linux手册页针对
signal()
:
的效果
signal()
在多线程程序未指定。因此,我认为这
sigaction()
是唯一可以在多线程过程中安全使用的方法。
那很有意思。在这种情况下,Linux手册页比POSIX更具限制性。POSIX指定signal()
:
如果进程是多线程的,或者进程是单线程的,并且执行了信号处理程序,则其结果不是:
- 的过程调用
abort()
,raise()
,kill()
,pthread_kill()
,或sigqueue()
,以产生没有被阻塞的信号- 待处理信号被解除阻塞并在解除阻塞的呼叫返回之前被传递
如果信号处理程序引用
errno
静态存储持续时间以外的任何对象,而不是通过为声明为的对象赋值,则该行为未定义volatile sig_atomic_t
,或者信号处理程序调用此标准中定义的任何函数,而不是其中列出的函数之一信号概念。
因此,POSIX明确指定signal()
了多线程应用程序中的行为。
但是,sigaction()
在几乎所有情况下都是首选- sigaction()
除非有绝大多数原因,否则应使用可移植的多线程代码(例如,“仅使用标准C定义的函数”),是的,C11代码可以是多线程的-线程)。基本上,这个答案的开头部分也说了这一点。
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
演示中,SIG_IGN
如果这是以前有意设置的内容,则避免将处理程序从ignore()更改。
对我而言,以下这一行足以决定:
sigaction()函数为控制信号提供了更全面,更可靠的机制。新应用程序应使用sigaction()而不是signal()
http://pubs.opengroup.org/onlinepubs/009695399/functions/signal.html#tag_03_690_07
无论您是从头开始还是修改旧程序,sigaction都是正确的选择。
signal()是标准C,sigaction()不是。
如果您可以使用任何一个(即您在POSIX系统上),请使用sigaction();。尚不确定signal()是否重置处理程序,这意味着要变得可移植,您必须在处理程序内部再次调用signal()。更糟糕的是,这是一场竞赛:如果您连续快速地收到两个信号,并且在重新安装处理程序之前传递了第二个信号,则将具有默认操作,这可能会杀死您的进程。 另一方面,保证sigaction()使用“可靠”的信号语义。您无需重新安装处理程序,因为它将永远不会被重置。使用SA_RESTART,您还可以获得一些系统调用以自动重启(因此您不必手动检查EINTR)。 sigaction() 具有更多选择并且可靠,因此鼓励使用它。
Psst ...告诉我的任何人都没有,但是POSIX当前具有bsd_signal()函数,其功能类似于signal(),但具有BSD语义,这意味着它是可靠的。它的主要用途是移植假定可靠信号的旧应用程序,而POSIX不建议使用它。
sigaction()
很好并且定义明确,但是它是Linux函数,因此仅在Linux上有效。 signal()
是不好的,定义不清,但是它是C标准函数,因此可以在任何东西上使用。
man 2 signal
(在此处在线查看)指出:
signal()的行为在UNIX版本之间有所不同,并且在历史上在Linux的不同版本中也有所不同。避免使用:
sigaction(2)
改为使用。请参阅下面的可移植性。可移植性signal()的唯一可移植用途是将信号的处置方式设置为SIG_DFL或SIG_IGN。使用signal()建立信号处理程序时的语义在整个系统中有所不同(并且POSIX.1明确允许这种变化);请勿将其用于此目的。
换句话说:不要使用signal()
。用sigaction()
,而不是!
兼容性说明:如上所述
signal
,应尽可能避免使用此功能。sigaction
是首选方法。
来源: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()
:
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);
//...
}
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()
使用示例:https : //www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handlingsigaction()
使用示例: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)
手册页:
描述
This signal() facility is a simplified interface to the more general sigaction(2) facility.
两者都调用相同的基础设施。您大概不应该同时处理单个信号的响应,但是将它们混合不会造成任何破坏。
我还建议在sign()上使用sigaction(),并希望再增加一点。sigaction()为您提供了更多选项,例如已终止进程的pid(可以使用siginfo_t结构)。
我会使用signal(),因为它在理论上至少更具可移植性。我将投票给任何可以提出一个没有POSIX兼容性层并支持signal()的现代系统的评论者。
从报价 GLIBC文档:
可以在单个程序中同时使用信号和信号功能,但您必须小心,因为它们可以以有些奇怪的方式进行交互。
与信号函数相比,sigaction函数指定的信息更多,因此信号的返回值不能表示sigaction可能性的全部范围。因此,如果使用signal保存并稍后重新建立操作,则它可能无法正确地重新建立通过sigaction建立的处理程序。
为避免产生问题,如果程序完全使用sigaction,请始终使用sigaction保存和恢复处理程序。由于sigaction比较笼统,因此它可以正确保存并重新建立任何动作,而不管它最初是使用信号还是sigaction建立的。
在某些系统上,如果您使用signal建立一个动作,然后使用sigaction检查它,则您获得的处理程序地址可能与您使用signal指定的地址不同。它甚至可能不适合用作带信号的动作参数。但是您可以依靠它作为sigaction的参数。在GNU系统上永远不会发生此问题。
因此,最好在一个程序中始终使用一种或另一种机制。
可移植性注意:基本信号功能是ISO C的功能,而签名是POSIX.1标准的一部分。如果您担心非POSIX系统的可移植性,则应改用信号功能。
版权所有(C)1996-2008 Free Software Foundation,Inc.
根据GNU自由文档许可版本1.2或自由软件基金会发布的任何更高版本,授予复制,分发和/或修改本文档的权限;没有不变的部分,没有前封面文字,也没有后封面文字。许可证的副本包含在标题为“ GNU自由文档许可证”的部分中。
从手册页信号(7)
可以将过程控制的信号传递到当前未阻塞信号的任何一个线程。如果多个线程中的信号畅通无阻,则内核选择一个任意线程来将信号传递到该线程。
我会说这个“问题”存在于signal(2)和sigaction(2)。因此,请注意信号和pthread。
...和signal(2)似乎在Linux中使用glibc 调用了sigaction(2)。
signal
实际上是Unix System V行为的描述。POSIX允许这种行为或更为理智的BSD行为,但是由于您不确定要获得哪种行为,因此仍然最好使用sigaction
。