Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
409 views
in Technique[技术] by (71.8m points)

signals - Explicitly invoke SIG_DFL/SIG_IGN handlers on Linux

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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...