【问题标题】:Facing issue with signal handlers for SIGTERM [duplicate]SIGTERM 的信号处理程序面临问题 [重复]
【发布时间】:2014-05-22 11:06:45
【问题描述】:

我的应用程序已经配置了一个SIGTERM 处理程序。 例如:

Signal(SIGTERM, &signalHandler);

我想在关机前进行一些处理,但我无法更改现有的信号处理程序。 为了做我的处理,我放了一个像这样的钩子 将我的处理程序配置为

signal(SIGTERM, &mySignalHandler);

mySignalHandler 在进行处理后调用signalHandler 以使系统即使在钩子之后也不受影响。

现在,考虑到mySignalHandler 将被调用,它在kill $pid 一次时起作用。 但如果我做两次或更多次。 signalHandler 被调用。据我所知SIG_DFL应该在这种情况下执行。

任何人都知道这样做的原因。

我正在使用“Red Hat Enterprise Linux AS 第 4 版(Nahant Update 8)”

[编辑]:测试程序面临的奇怪行为

#undef _GNU_SOURCE
using namespace std;

volatile sig_atomic_t signalHandlerVar_ = false;
volatile sig_atomic_t signalHandlerSecondVar_ = false;

extern "C" void signalHandler(int)
{
    signalHandlerVar_ = true;
}

extern "C" void signalHandlerSecond(int value)
{
    signalHandlerSecondVar_ = true;
}

void* threadFunc(void*)
{
    while(1)
    {
        if(signalHandlerVar_)
        {
            cout<<"signalHandler_ "<<signalHandlerVar_<<endl;
            signalHandlerVar_ = false;
        }

        if(signalHandlerSecondVar_)
        {
            cout<<"signalHandlerSecond_ "<<signalHandlerSecondVar_<<endl;
            signalHandlerSecondVar_ = false;
        }
    }

    return NULL;
}

int main()
{
    ::signal(SIGTERM, &signalHandler);
    ::signal(SIGTERM, &signalHandlerSecond);
    pthread_t thread;

    if(pthread_create(&thread, NULL, &threadFunc, NULL))
    {
        cout<<"pthread create failed"<<endl;
        return 1;
    }
    pthread_join(thread, NULL);
}

我面临的问题是,在每次杀死 $pid 时,它都会调用 signalHandlerSecond。因为我正在使用 #undef _GNU_SOURCE 并尝试了 #undef _BSD_SOURCE。但它应该将其重置为 SIG_DFL。

谁能指出我做错了什么?

【问题讨论】:

  • The man page 表示重置为 SIG_DFL 是原始的 Unix 行为。在 Linux 上,它具有 BSD 行为(如果 _BSD_SOURCE)或系统 V 行为(如果源被编译为严格标准)。如果您使用 -std=cXX 而不是 -std=gnuXX 进行编译,那么也许您可以尝试使用 GNU 功能进行编译,或者定义 _BSD_SOURCE

标签: c++ c linux


【解决方案1】:

来自signal

描述

signal() 的行为因 UNIX 版本而异,并且还具有 历史上在不同版本的 Linux 中有所不同。避免其 使用:使用 sigaction(2) 代替。请参阅下面的可移植性。
...
如果信号signum被传递给进程,那么其中之一 发生以下情况:
- ...
- ...
- 如果处置设置为函数,则首先 处置被重置为 SIG_DFL,或者信号被阻塞(参见 下面的可移植性),然后使用参数调用处理程序 签名。如果处理程序的调用导致信号 阻塞,则信号在从返回时解除阻塞 处理程序。

在 Linux 上,这取决于您如何编译源代码。由于您的程序没有被任何后续kill 中止,我怀疑它使用了稍后在可移植性中描述的 BSD 语义

Linux上的情况如下:

  * The kernel's signal() system call provides System V semantics.

  * By default, in glibc 2 and later, the signal() wrapper function
     does not invoke the kernel system call.  Instead, it calls
     sigaction(2) using flags that supply BSD semantics.  This default
     behavior is provided as long as the _BSD_SOURCE feature test macro
     is defined.  By default, _BSD_SOURCE is defined; it is also
     implicitly defined if one defines _GNU_SOURCE, and can of course be
     explicitly defined.

    On glibc 2 and later, if the _BSD_SOURCE feature test macro is not
     defined, then signal() provides System V semantics.  (The default
     implicit definition of _BSD_SOURCE is not provided if one invokes
     gcc(1) in one of its standard modes (-std=xxx or -ansi) or defines
     various other feature test macros such as _POSIX_SOURCE,
     _XOPEN_SOURCE, or _SVID_SOURCE; see feature_test_macros(7).)

要强制 System V 行为,您需要定义 _POSIX_SOURCE_XOPEN_SOURCE_SVID_SOURCE 之一,例如

gcc -D_SVID_SOURCE -Wall -g -pthread test.c

不过,这似乎不适用于 g++。当你用

编译时
g++ -D_SVID_SOURCE -Wall -g -pthread test.cpp

你仍然会得到 BSD 行为。添加-v

g++ -v -D_SVID_SOURCE -Wall -g -pthread test.cpp

揭示

/usr/lib/gcc/x86_64-linux-gnu/4.6/cc1plus -quiet -v -imultilib。 -imultiarch x86_64-linux-gnu -D_GNU_SOURCE -D_REENTRANT -D _SVID_SOURCE test.cpp -quiet -dumpbase test.cpp -mtune=generic -march=x86-64 -auxbase a -g -Wall -version -fstack-protector -o /tmp/cc3MpiO2.s

使用 gcc 编译时不存在。 你可以用一个额外的-U_GNU_SOURCE 来抑制它

g++ -U_GNU_SOURCE -D_SVID_SOURCE -Wall -g -pthread test.cpp

最后,这为我们提供了 System V 行为,这意味着信号处理程序在第一次信号传递后重置为 SIG_DFL

【讨论】:

  • “库重置信号,以便在再次出现相同信号时执行默认操作”。所以 signalHandler 不应该被调用。它给我的印象是在处理后它将 SIG_DFL 设置为我之前配置过。所以,内核做这样的簿记吗。
  • 内核不做任何记账。每个信号只存储一个信号处理程序。 signal 返回上一个信号处理程序。
  • 您的报价描述了 System V 的行为。这不是 Linux 所做的,除非您通过执行 #undef _BSD_SOURCE 或使用 -std=... 明确请求。尝试自己并使用 System V 语义编译您的程序。然后你会看到你引用的行为。
  • 编辑了帖子,尝试使用 Sysytem V 语义,现在遇到了一些奇怪的问题
  • 看来_BSD_SOURCE 没有明确定义,而是通过_GNU_SOURCE 隐式定义的,所以在这种情况下手册页是不正确的。请查看更新的答案。
【解决方案2】:

在你的函数 mySignalHandler() 结束时,你必须再次调用 signal():

第一次(代码中的任何地方):

signal(SIGTERM, &mySignalHandler);

然后在你的函数中:

void mySignalHandler() {
    //your code here

    signal(SIGTERM, &mySignalHandler);
}

这样,下次捕捉到SIGTERM,程序就会进入mySignalHandler()

希望对你有帮助。

【讨论】:

  • 真的应该用sigaction来做这个
  • 谢谢。希望可以尝试这项工作。但是为什么会调用信号处理程序,如果它已被处理为 SIG_DFL。这就是我想知道的。内核是否做任何簿记。
猜你喜欢
  • 1970-01-01
  • 2016-01-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-02
  • 2017-12-07
  • 1970-01-01
  • 2018-09-04
相关资源
最近更新 更多