sigaction和signal之间有什么区别?

Mat*_*ith 131 c posix signals

我正要为我们这里的应用程序添加一个额外的信号处理程序,我注意到作者已经习惯sigaction()了设置其他信号处理程序.我打算用signal().按照惯例我应该使用,sigaction()但如果我是从头开始写,我应该选择哪个?

Jon*_*ler 157

使用sigaction()除非你有非常令人信服的理由不这样做.

所述signal()接口具有对其有利古代(并且因此可用性),它是在C标准中定义.然而,它有许多不受欢迎的特性sigaction()可以避免 - 除非你使用明确添加的标志sigaction()来允许它忠实地模拟旧的signal()行为.

  1. signal()当前处理程序执行时,该函数不一定(必然)阻止其他信号到达; sigaction()可以阻止其他信号,直到当前处理程序返回.
  2. signal()功能(通常)将信号动作重置为SIG_DFL(默认)几乎所有信号.这意味着signal()处理程序必须重新安装自己作为其第一个操作.它还会在检测到信号和重新安装处理程序之间打开一个漏洞窗口,在此期间,如果信号的第二个实例到达,则会发生默认行为(通常是终止,有时带有偏见 - 也称为核心转储).
  3. signal()系统之间的确切行为有所不同 - 标准允许这些变化.

这些通常是使用sigaction()而不是使用的好理由signal().然而,界面sigaction()无疑更加繁琐.

你使用这两种中的哪,不要被诸如替代信号接口被诱惑 sighold(), sigignore(), sigpause()sigrelse().它们名义上是替代品sigaction(),但它们只是几乎没有标准化,并且存在于POSIX中以便向后兼容而不是严格使用.请注意,POSIX标准表明它们在多线程程序中的行为是未定义的.

多线程程序和信号是另一个复杂的故事. 据我所知,无论是signal()sigaction()在多线程应用程序确定.

Cornstalks 观察到:

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代码可以是多个-threaded).基本上这个答案的开头部分也是如此.

  • `signal`的描述实际上是Unix System V的行为.POSIX允许这种行为或更明智的BSD行为,但由于你不能确定你会得到哪一个,所以最好使用`sigaction`. (12认同)
  • @BulatM。如果您不能使用“ sigaction()”,那么您实际上必须对“ signal()”使用标准C规范。但是,这给您提供了极其贫困的选择机会。您可以:修改(volatile sig_atomic_t)类型的变量(文件作用域);调用“快速退出”函数之一(_Exit(),`quick_exit())或“ abort()”;使用当前信号编号作为信号参数调用`signal()`;返回。就是这样。不能保证其他任何东西都是可移植的。如此严格,以至于大多数人都忽略了这些规则,但是最终的代码却是狡猾的。 (2认同)
  • 来自 GCC 本身的优秀 `sigaction()` 演示:https://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.html;以及来自 GCC 本身的优秀 `signal()` 演示:https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling。请注意,在“signal”演示中,如果之前有意设置为忽略(“SIG_IGN”),他们会避免更改处理程序。 (2认同)

San*_*San 8

对我来说,以下这一行足以决定:

sigaction()函数为控制信号提供了更全面,更可靠的机制。新的应用程序应该使用sigaction()而不是signal()

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

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


Gab*_*les 6

简而言之:

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,应尽可能避免使用此功能。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()

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 /sf/ask/16233871/#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);

    //...
}
Run Code Online (Sandbox Code Playgroud)

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);
  …
}
Run Code Online (Sandbox Code Playgroud)

需要注意的主要链接:

  1. 标准信号:https : //www.gnu.org/software/libc/manual/html_node/Standard-Signals.html#Standard-Signals
    1. 终止信号:https : //www.gnu.org/software/libc/manual/html_node/Termination-Signals.html#Termination-Signals
  2. 基本信号处理,包括官方 GCCsignal()使用示例:https : //www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling
  3. 官方 GCCsigaction()使用示例:https : //www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.html
  4. 信号集,包括sigemptyset()sigfillset();我仍然不完全理解这些,但知道它们很重要:https : //www.gnu.org/software/libc/manual/html_node/Signal-Sets.html

也可以看看:

  1. TutorialsPoint C++ 信号处理【附优秀的演示代码】:https : //www.tutorialspoint.com/cplusplus/cpp_signal_handling.htm
  2. https://www.tutorialspoint.com/c_standard_library/signal_h.htm


idi*_*dak 5

它们是OS信号设备的不同接口.人们应该更喜欢使用sigaction来发出信号,因为signal()具有实现定义(通常易于竞争)的行为,并且在Windows,OS X,Linux和其他UNIX系统上表现不同.

请参阅此安全说明以获取详细信

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

小智 5

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

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

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