Home > Blockchain >  Linux - Why is a blocked and ignored signal pending?
Linux - Why is a blocked and ignored signal pending?

Time:09-26

When sending a signal to a process that both blocks and ignores it, the kernel still keeps this signal in the pending list (my terminology here). In this case the kernel behaves like the signal is only blocked, although it should also be ignored. I can't understand this behavior. Here is a C code for example with SIGUSR1 (which has the index 10):

#define _GNU_SOURCE
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

void handler(int sig)
{
    printf("Received signal %d\n", sig);
}

int main(int argc, char *argv[])
{           
    printf("PID is %ld\n", (long) getpid());
    
    int sig = SIGUSR1;
    
    //creating the sigaction struct and setting the handler
    struct sigaction act;
    act.sa_handler = handler;
    sigemptyset(&act.sa_mask);
    if(sigaction(sig, &act, NULL) == -1)
    {
        printf("Error: sigaction\n");
        exit(1);
    }
    
    //Blocking the signal
    sigset_t blockedSignals;
    sigemptyset(&blockedSignals);
    sigaddset(&blockedSignals, sig);
    printf("Blocking signal %d\n", sig);
    if(sigprocmask(SIG_SETMASK, &blockedSignals, NULL) == -1)
    {
        printf("Error: sigprocmask\n");
        exit(1);
    }
    
    //Ignoring the signal
    act.sa_handler = SIG_IGN;
    printf("Ignoring signal %d\n", sig);
    if(sigaction(sig, &act, NULL) == -1)
    {
        printf("Error: sigaction\n");
        exit(1);
    }
    
    //Sleeping for a while in order to give the user a chance to send the signal to this process
    printf("Sleeping for 20 sec. Please send the signal.\n");
    sleep(20);
    
    //Unblocking the signal
    /*sigemptyset(&blockedSignals);
    printf("Unblocking signal %d\n", sig);
    if(sigprocmask(SIG_SETMASK, &blockedSignals, NULL) == -1)
    {
        printf("Error: sigprocmask\n");
        exit(1);
    }*/
    
    //Let's check the pending list
    sigset_t pendingSignals;
    sigemptyset(&pendingSignals);
    if(sigpending(&pendingSignals) == -1)
    {
        printf("Error: sigpending\n");
        exit(1);
    }
    if(sigismember(&pendingSignals, sig) == 1)
    {
        printf("Signal %d is pending.\n", sig);
    }
    else
    {
        printf("Signal %d isn't pending.\n", sig);  
    }
        
    exit(0);
}

SIGUSR1 is both blocked and ignored. While this process sleeps, if I send a SIGUSR1 to it (from shell: kill -s SIGUSR1 PID), and then checks the pending list, I get this printing:

Signal 10 is pending.

If I uncomment the commented block of code, which unblocks the signal:

        sigemptyset(&blockedSignals);
        printf("Unblocking signal %d\n", sig);
        if(sigprocmask(SIG_SETMASK, &blockedSignals, NULL) == -1)
        {
            printf("Error: sigprocmask\n");
            exit(1);
        }

and repeat the experiment, I see the following printing:

Signal 10 isn't pending.

It's like the kernel gives priority to the 'blocking' over the 'ignoring'. Is it really the case?

Update: As far as I understand, when the process ignores a signal, it means that the kernel won't send it to the process. This also means that it won't keep it in the pending list. For example, if a signal is only blocked by the process, and exists in the pending list, and then we call 'sigaction' in order to ignore it, the kernel will remove this signal from the pending list. So the question is, why if we block ignore in ahead, the kernel inserts the signal to its pending list?

CodePudding user response:

Blocking a signal and ignoring it are two separate and independent things.

Ignoring a signal by setting its disposition to SIG_IGN instructs that when the signal is delivered the resulting action should be to do nothing.

Blocking a signal (by setting a signal mask that includes that signal) has the effect of preventing that signal from being delivered at all. If it is received, then it will remain pending until unblocked or the program terminates. Signal disposition does not matter until the signal is actually delivered. So,

It's like the kernel gives priority to the 'blocking' over the 'ignoring'. Is it really the case?

Yes. The effect of ignoring a signal cannot be realized while that signal is blocked.

With regard to the update to the question:

As far as I understand, when the process ignores a signal, it means that the kernel won't send it to the process.

No, that's incorrect. SIG_IGN is a signal disposition. That's what the program does in response to a signal. It can't respond if the kernel doesn't send the signal in the first place.

Note that another option for signal disposition is for the program to run a custom signal handler function. It should be clearer that this is something that the program does, not that the kernel does for it.

This also means that it won't keep it in the pending list.

It would mean that ignored signals never become pending, but your understanding of the semantics is incorrect.

So the question is, why if we block ignore from ahead, the kernel inserts the signal to its pending list?

Because that's what the kernel does with signals. You can characterize it as what the kernel does with all signals, but those that aren't blocked don't stay pending very long.

  • Related