clinux-kernelsignalsposixipc

Why doesn't SigPnd change in /proc/<pid>/status?


I am trying to understand the signals in POSIX/Linux. I wrote this test.c program:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>

void handler(int signum) {
    printf("\nGoodbye!\n"); exit(0);
}

int main() {
    struct sigaction sa = { .sa_handler = handler };

    // Intercept SIGINT
    sigemptyset(&sa.sa_mask);
    if (sigaction(SIGINT, &sa, NULL) == -1) {
        perror("sigaction"); exit(EXIT_FAILURE);
    }

    signal(SIGUSR1, SIG_IGN); // Ignore SIGUSR1

    // Block SIGUSR2
    sigset_t mask;
    sigemptyset(&mask);
    sigaddset(&mask, SIGUSR2);
    sigprocmask(SIG_BLOCK, &mask, NULL);

    // Simulate work for 10 seconds
    for (int i = 0; i < 10; i++) {
        printf("Working... %d\n", i);
        sleep(1);
    } 
    sigprocmask(SIG_UNBLOCK, &mask, NULL);
}

During the execution, I send:

kill -s USR2 $(pgrep test)

Then quickly after that:

cat /proc/`pgrep test`/status | grep Sig
SigQ:   1/256970
SigPnd: 0000000000000000
SigBlk: 0000000000000800
SigIgn: 0000000000000200
SigCgt: 0000000000000002

SigCgt shows me that SIGINT is intercepted, SigIgn shows that SIGUSR1 is ignored and SigBlk shows that SIGUSR2 is blocked.

In my understanding, sending a signal to a process that is blocked by the process will be pending until the unblock. However I don't see any effect on SigPnd

Any explanation?


Solution

  • You need to look at ShdPnd (signals pending directed to the whole process), not SigPnd (signals pending directed to that specific thread):

    $ kill -s USR2 $(pgrep test)
    $ cat /proc/`pgrep test`/status | grep -E 'Sig|Shd'
    SigQ:   1/256970
    SigPnd: 0000000000000000
    ShdPnd: 0000000000000800
    SigBlk: 0000000000000800
    SigIgn: 0000000000000200
    SigCgt: 0000000000000002
    

    The kill command uses the kill syscall, which directs the signal to the whole process. To have a signal appear in SigPnd in /proc/PID/status you would have to direct it specifically to the main thread using the tgkill syscall (not sure if there is a command that can do this for you).