一尘不染

在Linux上显式调用SIG_DFL / SIG_IGN处理程序

linux

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

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_DFL或,该如何手动调用它SIG_IGN?另请注意,该SIG_IGN定义为1


阅读 261

收藏
2020-06-07

共1个答案

一尘不染

当你发现你不能调用SIG_DFL和SIG_IGN 本身 。但是,您可以或多或少模仿它们的行为。

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

  • 用户定义的sa_handlers 很容易
  • 对于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()也可以。

2020-06-07