Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Explanation of sigsuspend needed

I need a clarification on sigsuspend topic. I have a simplified example

sigset_t mask, oldmask;
sigemptyset (&mask);
sigaddset (&mask, SIGRTMIN+1);
sigprocmask (SIG_BLOCK, &mask, &oldmask);

sigsuspend(&oldmask);

sigprocmask (SIG_UNBLOCK, &mask, NULL);

Here is how I understand this:

  • sigemptyset clears a list of signals (mask) - because it is initialized with currently blocked signals (?)
  • sigaddset adds SIGRTMIN+1 to mask
  • sigprocmask sets all signals in mask+oldmask to be blocked
  • sigsuspend(&oldmask) suspends thread execution till one of the blocked signals arrives ? Shouldn't there be sigsuspend(&mask) if I want to block SIGRTMIN+1?

    Secondly, let's suppose I have such sigsuspend in a loop and there arrived a few SIGRTMIN+1 signals. Will such loop continue for each signal ? in some kind of queue?

    while(1){
        sigsuspend(&oldmask)
        printf("recieved signal");
    }
    

    so that for each signal I have 'recieved signal' printed?

  • like image 358
    xwhyz Avatar asked Mar 07 '13 23:03

    xwhyz


    1 Answers

    Major parts of the previous edition of this answer were wrong. I apologize for my mistake. I thank Wotim for pointing out the error.

    POSIX's definition of sigsuspend()

    The POSIX standard describes sigsuspend() fairly clearly:

    #include <signal.h>

    int sigsuspend(const sigset_t *sigmask);

    DESCRIPTION

    The sigsuspend() function shall replace the current signal mask of the calling thread with the set of signals pointed to by sigmask and then suspend the thread until delivery of a signal whose action is either to execute a signal-catching function or to terminate the process. This shall not cause any other signals that may have been pending on the process to become pending on the thread.

    If the action is to terminate the process then sigsuspend() shall never return. If the action is to execute a signal-catching function, then sigsuspend() shall return after the signal-catching function returns, with the signal mask restored to the set that existed prior to the sigsuspend() call.

    It is not possible to block signals that cannot be ignored. This is enforced by the system without causing an error to be indicated.

    RETURN VALUE

    Since sigsuspend() suspends thread execution indefinitely, there is no successful completion return value. If a return occurs, -1 shall be returned and errno set to indicate the error.

    ERRORS

    The sigsuspend() function shall fail if:

    [EINTR] A signal is caught by the calling process and control is returned from the signal-catching function.

    Also, the POSIX Signal concepts says:

    Each thread has a "signal mask" that defines the set of signals currently blocked from delivery to it.

    Application to your code fragments

    The sigsuspend() function is the modern analogue of the old pause() function.

    Major change: two code blocks reversed.

    It allows you to send your thread to sleep until one of its selected signals occurs. If you want it to sleep until SIGRTMIN+1 arrives, you use:

    sigfillset(&mask);
    sigdelset(&mask, SIGRTMIN+1);
    sigsuspend(&mask);
    

    This blocks every signal except SIGRTMIN+1.

    If you want to sleep until a signal other than SIGRTMIN+1 arrives, you use:

    sigemptyset(&mask);
    sigaddset(&mask, SIGRTMIN+1);
    sigsuspend(&mask);
    

    This blocks only SIGRTMIN+1.

    Your loop won't print Received signal reliably unless you add a newline at the end (and maybe not even then; you might need to fflush(stdout) or equivalent). However, if you have SIGRTMIN+1 blocked in the ordinary course of events and then you set up &oldmask to allow only SIGRTMIN+1, then your signals will be reliably delivered while your code is in sigsuspend().

    Your description of sigprocmask() is not correct:

    sigprocmask sets all signals in mask+oldmask to be blocked

    When you do:

    sigset_t mask, oldmask;
    sigemptyset(&mask);
    sigaddset(&mask, SIGRTMIN+1);
    sigprocmask(SIG_BLOCK, &mask, &oldmask);
    

    You add SIGRTMIN+1 to the list of blocked signals (because you used SIG_BLOCK and mask only contains SIGRTMIN+1). The output in oldmask is the signal mask before SIGRTMIN+1 was added. It might or might not already have contained SIGRTMIN+1. The process's signal mask is changed (if you needed to set the signal mask for a thread, you'd use pthread_sigprocmask() instead) from its current value to a new value containing SIGRTMIN+1, and you get told the old value.

    From the comments

    So is it wrong to use sigsuspend(&oldmask) as in my first example if I want to block SIGRTMIN+1?

    Major change.

    Yes, it is wrong. Calling sigsuspend(&oldmask) sends your thread to sleep until one of the signals in oldmask is received. The content of oldmask is the set of signals that was blocked before you added SIGRTMIN+1.

    Yes, it is wrong. Calling sigsuspend(&oldmask) sends your thread to sleep until one of the signals not in oldmask is received. The content of oldmask is the set of signals that was blocked before you added SIGRTMIN+1.

    The following two paragraphs are withdrawn rather than necessarily condemned as wrong. I think there are elements of truth in both, though there is room for improvement in both, too.

    You do not use sigsuspend() to block signals per se; you use it to wait for one of a specified set of signals to arrive.

    If you want to ignore SIGRTMIN+1, then you need to use sigaction() or you can use sigprocmask() as you did; that specifically blocks SIGRTMIN+1 as well as all the other signals that were already blocked.

    Do I understand correctly that it will block the same signals the way it was?

    Assuming 'it' is sigsuspend(), then it will block any signal not in oldmask and wait for one of the signals in oldmask to arrive.

    Assuming 'it' is sigsuspend(), then it will block any signal in oldmask and wait for one of the signals not in oldmask to arrive.

    And if I do sigsuspend(&mask) then it will block SIGRTMIN+1 and all of the signals in oldmask because of sigprocmask(SIG_BLOCK, &mask, &oldmask)?

    Major change

    Absolutely not! This will suspend the thread until one of the signals in mask arrives. Since the only signal in mask is SIGRTMIN+1, only when that arrives will sigsuspend() return. The sigprocmask() reported what was being blocked before the call in oldmask; it did not modify mask (your variable) at all; it did modify the process's signal mask by adding SIGRTMIN+1.

    Absolutely not! This will suspend the thread until one of the signals not in mask arrives. Since the only signal in mask is SIGRTMIN+1, only that signal is blocked and when any other signal arrives, it will be handled and sigsuspend() will return. The sigprocmask() reported what was being blocked before the call in oldmask; it did not modify mask (your variable) at all; it did modify the process's signal mask by adding SIGRTMIN+1.


    I strongly recommend reading, or rereading, the POSIX Signal concepts and the various functions that manipulate signal handling. (And yes, this is, in part, a prescription of "Physician, heal thyself".)

    Please let me know if there are still mistakes in here.

    like image 132
    Jonathan Leffler Avatar answered Oct 11 '22 03:10

    Jonathan Leffler