代码之家  ›  专栏  ›  技术社区  ›  Matt Joiner

在Linux上显式调用sig_dfl/sig_ign处理程序

  •  1
  • Matt Joiner  · 技术社区  · 15 年前

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

    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_IGN ?还要注意 西格格 定义为 1 .

    3 回复  |  直到 12 年前
        1
  •  2
  •   pilcrow    12 年前

    正如您发现的那样,您不能调用sig_dfl和sig_ign 本身 . 然而,你可以或多或少地模仿他们的行为。

    简言之,模拟正常信号配置将是:

    • 用户定义很容易 sa_handler S
    • 对于sig ou 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:为什么不发出信号,不管怎样都会被忽略。

    与三个系统调用(重新提升、取消屏蔽、重新屏蔽)相比,noop在用户空间中的效率稍高。此外,当sig_被忽略时,chld具有特殊的语义。

    3:为什么要特别治疗西格尔德?

    最初(检查答案编辑)我没有——在sig-ign案例中重新提出它, 因为 IGNored CHLD signals tell the kernel to automatically reap children .

    但是,我改变它是因为 "natural" CHLD signals carry information about the terminated process (至少是PID、状态和实际UID)。 用户生成的chld信号没有相同的语义,在我的测试中, 忽略它们不会导致2.6自动调整排队的僵尸的信号。 被“错过”了。所以,我自己做。

    4:为什么与“停止”相关的信号unblocking cont不会调用默认的cont处理程序unstop进程?

    如果我们停止(不执行)并且cont被阻止,我们将永远不会收到 唤醒我们的信号!

    5:为什么不叫加薪而不是你给的杀戮线?

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

        2
  •  1
  •   Dimitri    15 年前

    我在你的代码中看到两个错误:
    1)您应该颠倒最后两行,如下所示:

    act.sa_handler(signum);
    sigaction(signum, NULL, &act);
    

    2)必须将函数处理程序传递给fiedl sa_处理程序,而不是int。函数的原型应如下所示:

       /**
        *some where in you code
         */
         void handler (int signal){ /*your code*/}
       /**
        *
        */      
        act.sa_handler = handler;
    

    如果希望调用默认处理程序,则应设置字段 萨哈德勒 西格尔德福 它应该是有效的。

        3
  •  1
  •   Dummy00001    15 年前

    我不知道怎么做。

    我唯一的建议是调查 man 7 signal 并根据您看到的表手动执行操作。 Ign 什么也不是。 Core 是呼叫 abort() . Term _exit() .

    当然,您也可以将信号处理程序设置回sig_dfl,然后 kill(getpid(),THE_SIG) (或其等效物) raise(THE_SIG) )(我个人不喜欢 raise 因为在某些系统上,它可能会在stderr上生成一些消息。)