在Linux上显式调用SIG_DFL/SIG_IGN处理程序 [英] Explicitly invoke SIG_DFL/SIG_IGN handlers on Linux

查看:223
本文介绍了在Linux上显式调用SIG_DFL/SIG_IGN处理程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已阻止,然后通过以下代码等待信号:

sigset_t set;
sigfillset(&set); // all signals
sigprocmask(SIG_SETMASK, &set, NULL); // block all signals
siginfo_t info;
int signum = sigwaitinfo(&set, &info); // wait for next signal
struct sigaction act;
sigaction(signum, NULL, &act); // get the current handler for the signal
act.sa_handler(signum); // invoke it

最后一行生成分段错误,因为处理程序设置为SIG_DFL(定义为0).如果将默认处理程序设置为SIG_DFLSIG_IGN,我该如何手动调用它?另请注意,SIG_IGN被定义为1.

解决方案

您发现您无法调用SIG_DFL和SIG_IGN 本身.但是,您可以或多或少地模仿它们的行为.

简而言之,模仿正常的信号处置将是:

  • 对于用户定义的sa_handler s
  • 非常简单
  • 对于SIG_IGN来说足够容易,需要注意的是,对于CHLD,您需要waitpid()
  • 直截了当,但对SIG_DFL感到不愉快,重新进行了升级,让内核发挥其魔力.

这是您想要的吗?

#include <signal.h>
#include <stdlib.h>

/* Manually dispose of a signal, mimicking the behavior of current
 * signal dispositions as best we can.  We won't cause EINTR, for
 * instance.
 *
 * FIXME:  save and restore errno around the SIG_DFL logic and
 *         SIG_IGN/CHLD logic.
 */
void dispatch_signal(const int signo) {
    int stop = 0;
    sigset_t oset;
    struct sigaction curact;

    sigaction(signo, NULL, &curact);

    /* SIG_IGN => noop or soak up child term/stop signals (for CHLD) */
    if (SIG_IGN == curact.sa_handler) {
        if (SIGCHLD == signo) {
          int status;
          while (waitpid(-1, &status, WNOHANG|WUNTRACED) > 0) {;}
        } 
        return;
    }

    /* user defined => invoke it */
    if (SIG_DFL != curact.sa_handler) {
        curact.sa_handler(signo);
        return;
    }

    /* SIG_DFL => let kernel handle it (mostly).
     *
     *  We handle noop signals ourselves -- "Ign" and "Cont", which we
     *  can never intercept while stopped.
     */
    if (SIGURG == signo || SIGWINCH == signo || SIGCONT == signo) return;

    /*  Unblock CONT if this is a "Stop" signal, so that we may later be
     *  woken up.
     */
    stop = (SIGTSTP == signo || SIGTTIN == signo || SIGTTOU == signo);
    if (stop) {
        sigset_t sig_cont;

        sigemptyset(&sig_cont);
        sigaddset(&sig_cont, SIGCONT);
        sigprocmask(SIG_UNBLOCK, &sig_cont, &oset);
    }

    /*  Re-raise, letting the kernel do the work:
     *     - Set exit codes and corefiles for "Term" and "Core"
     *     - Halt us and signal WUNTRACED'ing parents for "Stop"
     *     - Do the right thing if we forgot to handle any special
     *       signals or signals yet to be introduced
     */
    kill(getpid(), signo);

    /* Re-block CONT, if needed */
    if (stop) sigprocmask(SIG_SETMASK, &oset, NULL);
}


更新 (针对OP的精彩问题)

1:此插槽是否在sigwaitinfo之后插入?

是的.像这样:

... block signals ...
signo = sigwaitinfo(&set, &info);
dispatch_signal(signo);

2:为什么不引发由SIG_IGN处理的信号,无论如何它们都会被忽略

与三个系统调用(重新引发,取消屏蔽,重新屏蔽)相比,在用户空间进行noop效率更高.此外,当SIG_IGNored时,CHLD具有特殊的语义.

3:为什么要特别对待SIGCHLD?

最初(检查答案),我没有-在SIG_IGN情况下重新提出, 因为 IGNored CHLD信号告诉内核自动获取子进程.

但是,我进行了更改,因为自然" CHLD信号携带有关以下内容的信息 终止的进程(至少是PID,状态和实际UID). 用户生成的CHLD信号没有相同的语义,在我的测试中, IGNoring它们不会导致2.6自动获取其SIGCHLD的排队的僵尸 被遗漏了".所以,我自己做.

4:为什么与停止"相关的信号会阻止CONT.不会为CONT调用默认处理程序吗?

如果我们停止(不执行)并且CONT被阻止,我们将永远不会收到 叫醒我们!

5:为什么不叫加注而不是您给的加注线?

个人偏好; raise()也可以.

I've blocked, and then waited for a signal via the following code:

sigset_t set;
sigfillset(&set); // all signals
sigprocmask(SIG_SETMASK, &set, NULL); // block all signals
siginfo_t info;
int signum = sigwaitinfo(&set, &info); // wait for next signal
struct sigaction act;
sigaction(signum, NULL, &act); // get the current handler for the signal
act.sa_handler(signum); // invoke it

The last line generates a segmentation fault, as the handler is set to SIG_DFL (defined as 0). How can I manually invoke the default handler if it's set to SIG_DFL or SIG_IGN? Also note that SIG_IGN is defined as 1.

解决方案

As you discovered you cannot invoke SIG_DFL and SIG_IGN per se. However, you can more-or-less mimic their behavior.

Briefly, imitating normal signal disposition would be:

  • quite easy for user-defined sa_handlers
  • easy enough for SIG_IGN, with the caveat that you'd need to waitpid() in the case of CHLD
  • straightforward but unpleasant for SIG_DFL, re-raising to let the kernel do its magic.

Does this do what you want?

#include <signal.h>
#include <stdlib.h>

/* Manually dispose of a signal, mimicking the behavior of current
 * signal dispositions as best we can.  We won't cause EINTR, for
 * instance.
 *
 * FIXME:  save and restore errno around the SIG_DFL logic and
 *         SIG_IGN/CHLD logic.
 */
void dispatch_signal(const int signo) {
    int stop = 0;
    sigset_t oset;
    struct sigaction curact;

    sigaction(signo, NULL, &curact);

    /* SIG_IGN => noop or soak up child term/stop signals (for CHLD) */
    if (SIG_IGN == curact.sa_handler) {
        if (SIGCHLD == signo) {
          int status;
          while (waitpid(-1, &status, WNOHANG|WUNTRACED) > 0) {;}
        } 
        return;
    }

    /* user defined => invoke it */
    if (SIG_DFL != curact.sa_handler) {
        curact.sa_handler(signo);
        return;
    }

    /* SIG_DFL => let kernel handle it (mostly).
     *
     *  We handle noop signals ourselves -- "Ign" and "Cont", which we
     *  can never intercept while stopped.
     */
    if (SIGURG == signo || SIGWINCH == signo || SIGCONT == signo) return;

    /*  Unblock CONT if this is a "Stop" signal, so that we may later be
     *  woken up.
     */
    stop = (SIGTSTP == signo || SIGTTIN == signo || SIGTTOU == signo);
    if (stop) {
        sigset_t sig_cont;

        sigemptyset(&sig_cont);
        sigaddset(&sig_cont, SIGCONT);
        sigprocmask(SIG_UNBLOCK, &sig_cont, &oset);
    }

    /*  Re-raise, letting the kernel do the work:
     *     - Set exit codes and corefiles for "Term" and "Core"
     *     - Halt us and signal WUNTRACED'ing parents for "Stop"
     *     - Do the right thing if we forgot to handle any special
     *       signals or signals yet to be introduced
     */
    kill(getpid(), signo);

    /* Re-block CONT, if needed */
    if (stop) sigprocmask(SIG_SETMASK, &oset, NULL);
}


UPDATE (in response to OP's excellent questions)

1: does this slot in after the sigwaitinfo?

Yes. Something like:

... block signals ...
signo = sigwaitinfo(&set, &info);
dispatch_signal(signo);

2: Why not raise those signals handled by SIG_IGN, they'll be ignored anyway

It's slightly more efficient to noop in userspace than by three syscalls (re-raise, unmask, re-mask). Moreover, CHLD has special semantics when SIG_IGNored.

3: Why treat SIGCHLD specially?

Originally (check answer edits) I didn't -- re-raised it in the SIG_IGN case, because IGNored CHLD signals tell the kernel to automatically reap children.

However, I changed it because "natural" CHLD signals carry information about the terminated process (at least PID, status, and real UID). User-generated CHLD signals don't carry the same semantics, and, in my testing, IGNoring them doesn't cause 2.6 to autoreap queued zombies whose SIGCHLD was "missed." So, I do it myself.

4: Why are "stop" related signals unblocking CONT. Will not invoking the default handler for CONT unstop the process?

If we're stopped (not executing) and CONT is blocked, we will never receive the signal to wake us up!

5: Why not call raise instead of the kill line you've given?

Personal preference; raise() would work, too.

这篇关于在Linux上显式调用SIG_DFL/SIG_IGN处理程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆