我将要在此处的应用程序中添加一个额外的信号处理程序,并且我注意到作者使用sigaction()来设置其他信号处理程序。我打算使用signal()。要遵循惯例,我应该使用sigaction(),但是如果我是从头开始编写的,应该选择哪个?

#1 楼

除非有非常令人信服的理由,否则请使用sigaction()
signal()接口有利于古代(因此具有可用性),并且在C标准中进行了定义。但是,它具有许多sigaction()可以避免的不良特性-除非您使用显式添加到sigaction()的标志以使其忠实地模拟旧的signal()行为。

signal()函数不会(必要)阻止当前处理程序正在执行时到达的其他信号; sigaction()可以阻止其他信号,直到当前处理程序返回为止。
signal()函数(通常)会将几乎所有信号的信号动作重置回SIG_DFL(默认值)。这意味着signal()处理程序必须首先重新安装自身。在检测到信号到重新安装处理程序之间,这还会打开一个漏洞窗口,在此期间,如果信号的第二个实例到达,则会发生默认行为(通常会终止,有时会有偏见-aka核心转储)。 br /> 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代码可以是多线程的)。基本上,这个答案的开头段落也这样说。

评论


信号的描述实际上是Unix System V行为的描述。 POSIX允许这种行为或更为理智的BSD行为,但是由于您不确定要获得哪种行为,因此最好还是使用sigaction。

–R .. GitHub停止帮助ICE
2011年8月15日下午4:08

除非您使用显式添加到sigaction()的标志,以使其能够忠实地模拟旧的signal()行为。这些标记(特定地)是什么?

–克里斯蒂安·库瓦斯(ChristianCuevas)
14-10-30在1:20



@AlexFritz:主要是SA_RESETHAND,也有SA_NODEFER。

–乔纳森·莱弗勒(Jonathan Leffler)
2014年10月30日,下午1:28

@BulatM。如果您不能使用sigaction(),则实际上必须对signal()使用标准C规范。但是,这给您提供了极其贫困的选择机会。您可以:修改volatile sig_atomic_t类型的(文件作用域)变量;调用“快速退出”函数之一(_Exit(),quick_exit())或abort();使用当前信号号作为信号参数调用signal();返回。就是这样。不能保证其他任何东西都是可移植的。如此严格,以至于大多数人都忽略了这些规则,但是所产生的代码却是狡猾的。

–乔纳森·莱弗勒(Jonathan Leffler)
16-10-27在16:14

来自GCC本身的出色sigaction()演示:gnu.org/software/libc/manual/html_node/…;以及来自GCC的出色的signal()演示:gnu.org/software/libc/manual/html_node/…。请注意,在信号演示中,如果以前是故意将其设置为,则它们避免将处理程序更改为ignore(SIG_IGN)。

–加百利·斯台普斯
19年11月12日在3:21



#2 楼

对我来说,下面这行代码足以决定:


sigaction()函数提供了一种更全面和可靠的信号控制机制;新的
应用程序应使用sigaction()
而不是signal()


http://pubs.opengroup.org/onlinepubs/009695399/functions/signal。 html#tag_03_690_07

无论是从头开始还是修改旧程序,sigaction应该是正确的选择。

#3 楼

它们是OS信号设备的不同接口。如果可能的话,应该更倾向于使用sigaction来发出信号,因为signal()具有实现定义的(通常容易发生竞争)行为,并且在Windows,OS X,Linux和其他UNIX系统上的行为有所不同。

请参阅此安全性有关详细信息,请注意。

评论


我只是看了glibc源代码,而signal()只是调用了sigaction()。另请参见上面的MacOS手册页声明相同的地方。

–bmdhacks
08-10-24在0:40

很高兴知道。我只见过信号处理程序在退出之前用来整洁地关闭东西,所以我通常不会依靠行为来重新安装处理程序。

–马修·史密斯(Matthew Smith)
08-10-24在1:43

#4 楼

signal()是标准C,而sigaction()不是。

如果您可以使用任何一个(即,您在POSIX系统上),请使用sigaction();。尚不确定signal()是否重置处理程序,这意味着要移植,您必须在处理程序内部再次调用signal()。更糟糕的是,这是一场竞赛:如果您连续快速地收到两个信号,并且在重新安装处理程序之前传递了第二个信号,则您将具有默认操作,这很可能会杀死您的进程。
sigaction()使用“可靠”的信号语义。您无需重新安装处理程序,因为它将永远不会被重置。使用SA_RESTART,您还可以获得一些系统调用以自动重新启动(因此您不必手动检查EINTR)。
sigaction()具有更多选项并且可靠,因此鼓励使用它。

Psst ...不要告诉我我告诉过您的任何人,但是POSIX当前具有bsd_signal()函数,其功能类似于signal(),但具有BSD语义,这意味着它是可靠的。它的主要用途是移植假定可靠信号的旧应用程序,而POSIX不建议使用它。

评论


POSIX没有功能bsd_signal()-一些POSIX实现可能具有该功能,但是POSIX本身不包含该功能(请参阅POSIX)。

–乔纳森·莱弗勒(Jonathan Leffler)
19年8月13日在16:22

#5 楼

简而言之:

sigaction()很好并且定义明确,但是它是Linux函数,因此仅在Linux上有效。 signal()不好并且定义不清,但是它是C标准函数,因此可以在任何东西上使用。

Linux手册页对此有何评价?

man 2 signal (在此处在线查看)状态:


signal()的行为在UNIX版本之间有所不同,并且在历史上在Linux的不同版本中也有所不同。避免使用它:改用sigaction(2)。请参见下面的可移植性。

可移植性
signal()的唯一可移植用法是将信号的处置方式设置为
SIG_DFL或SIG_IGN。使用signal()建立
信号处理程序时的语义在整个系统中有所不同(并且POSIX.1明确允许
这种变化);不要为此目的使用它。


换句话说:不要使用signal()。请改用sigaction()

GCC的想法是什么? signal是首选方法。


来源:https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-处理

因此,如果Linux和GCC都说不使用sigaction,而是使用signal(),那就引出了一个问题:我们如何使用这个令人困惑的sigaction()东西!?

使用示例:

在此处阅读GCC的出色sigaction()示例:https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal -处理

及其出色的signal()示例: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 https://stackoverflow.com/questions/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.对于sigaction()来说,尽管如上所述不是连接信号处理程序的好方法,但是仍然知道如何使用它仍然很好。

这是复制粘贴的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);
  …
}


要注意的主要链接:


标准信号:https://www.gnu.org/software/libc/manual/html_node/Standard-Signals.html#Standard-Signals


终端信号:https://www.gnu .org / software / libc / manual / html_node / Termination-Signals.html#Termination-Signals



基本信号处理,包括官方GCC signal()使用示例:https:/ /www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling

官方GCC signal()使用示例:https://www.gnu。 org / software / libc / manual / html_node / Sigaction-Function-Example.html

信号集,包括sigaction()sigemptyset();我仍然不十分了解这些内容,但知道它们很重要:https://www.gnu.org/software/libc/manual/html_node/Signal-Sets.html


另请参见:


TutorialsPoint C ++信号处理[具有出色的演示代码]:https://www.tutorialspoint.com/cplusplus/cpp_signal_handling.htm

https ://www.tutorialspoint.com/c_standard_library/signal_h.htm


#6 楼

signal(3)手册页:


Description

 This signal() facility is a simplified interface to the more
 general sigaction(2) facility.



两者都调用相同的基础设施。您大概不应该同时处理单个信号的响应,但是将它们混合不会造成任何破坏...

评论


那不在我的手册页中!我得到的只是“描述Signal()系统调用为带有数字符号的信号安装一个新的信号处理程序。”我需要升级到有用的手册页包。

–马修·史密斯(Matthew Smith)
08-10-23在23:35

这与Mac OS X 10.5页面不符。

– dmckee ---前主持人小猫
08-10-23在23:47

还从glibc的源代码进行了验证。 signal()只是调用sigaction()

–bmdhacks
08-10-24在0:39

但是,并非在信号的所有实现上都是如此。如果您要强制执行“自愿”行为,请不要依赖此假设。

–本·伯恩斯
2012年2月21日在21:12

#7 楼

我还建议在signal()上使用sigaction(),并希望再增加一个点。
sigaction()为您提供了更多选择,例如死掉的进程的pid(可以使用siginfo_t结构)。 />

#8 楼

我会使用signal(),因为它在理论上至少更具可移植性。我将投票给任何能够提出一个没有POSIX兼容性层并支持signal()的现代系统的评论者。

引用GLIBC文档的话:


可以在单个程序中同时使用信号和信号函数,但是您必须小心,因为它们可以以稍微奇怪的方式进行交互。

函数指定更多的信息,因此信号的返回值不能表示sigaction可能性的全部范围。因此,如果您使用信号
保存并稍后重新建立操作,则它可能无法正确地重新建立使用sigaction建立的处理程序。

为避免出现问题因此,如果您的程序完全使用sigaction,请始终使用sigaction保存并还原处理程序。由于
sigaction比较笼统,因此它可以正确保存并重新建立任何
动作,而不管它最初是使用
signal还是sigaction建立的。

在某些系统上如果您使用signal建立一个动作,然后与signal指定的地址不同。
甚至可能不适合用作带信号的动作参数。但是您可以依靠
将其用作sigaction的参数。这个问题永远不会在GNU系统上发生。

因此,最好在一个程序中一致地使用一种或另一种机制。

可移植性注意:基本信号功能是ISO C的功能,
签名是POSIX.1标准的一部分。如果您
担心到非POSIX系统的可移植性,则应该
使用信号功能。


版权所有(C)1996-2008 Free Software Foundation,Inc.

根据GNU Free Documentation License的条款,授予复制,分发和/或修改此文档的权限。 /> 1.2版或自由软件基金会发布的任何更高版本
;没有不变的部分,没有前封面文字,
,也没有后封面文字。许可证的副本包含在标题为“ GNU自由文档许可证”的部分中。



#9 楼

从手册页上的signal(7)


进程控制的信号可以传递到当前未阻塞信号的任何线程之一。
如果多个线程的信号畅通无阻,则
内核选择一个任意线程来将信号传递到该线程。


我会说这个“问题”存在signal(2)和sigaction(2)。
所以要小心信号和pthreads。

...,而signal(2)似乎在Linux下调用了sigaction(2)使用glibc。