Search code examples
cunixsignalsposixipc

Waiting for a signal inside the signal handler itself


I was trying to write a program which involves sending signal to a process for notifying it to pause for some time and start work again after another signal is received. I wrote a signal handler like this:

void sig_handler(int alrm)
{
    if(sig_rcv==0)
    {
        printf("Signal received..sig_rcv value: %d\n",sig_rcv);
        sig_rcv = (sig_rcv+1)%2;
        printf("After increment sig_rcv value: %d\n",sig_rcv);
        signal(SIGUSR1,sig_handler);
        if(pause()<0)
        {
            printf("Signal received again..\n");
        }
    }
    else
    {
        sig_rcv = (sig_rcv+1)%2;
        printf("Signal received..sig_rcv value: %d\n",sig_rcv);
        signal(SIGUSR1,sig_handler);
    }
    printf("Exiting..\n");
}

Here I am maintaining a global variable sig_rcv which is initially 0 and if a signal is received when it is zero, it will go to the if condition and pause for another signal.On the other hand, if it gets a signal while sig_rcv is 1, it will just change the value of that variable. My purpose of writing the signal handler in this way was to use the same signal for two different purposes. I used this system call:

signal(SIGUSR1,sig_handler);

Now when I was sending SIGUSR1 to the process it is executing upto the pause()statement. But after sending SIGUSR1 for the second time, it shows no effect.It just freezes at the pause statement. This problem was resolved after I used another signal for removing the pause state, something like:

void sig_handler2(int a)
{
    sig_rcv = (sig_rcv+1)%2;
    printf("Restarting reading...\n");
}

and

signal(SIGUSR2,sig_handler2);

It worked perfectly in this case. So, my question is, why is this phenomenon taking place? Can't we wait for a signal while executing the signal handler written for the same signal? If it is so, what is the reason? And is there any way of achieving the same result without using SIGUSR2?


Solution

  • Since you're using signal rather than sigaction, it's important to note the following from the POSIX standard:

    When a signal occurs, and func points to a function, it is implementation-defined whether the equivalent of a:

    signal(sig, SIG_DFL);

    is executed or the implementation prevents some implementation-defined set of signals (at least including sig) from occurring until the current signal handling has completed.

    This reflects the two historic signal implementations, SVID and BSD. Since you're using Ubuntu 18.04, you're likely using glibc, which implements the latter (BSD) semantics. SIGUSR1 is masked while its handler is executing.

    Since you want the SVID semantics, in which no signals are masked and you need to reestablish the signal handler each time the signal handler is called, you should replace your signal(SIGUSR1, sig_handler); calls with the following:

    struct sigaction sa = { .sa_handler = sig_handler, .sa_flags = SA_NODEFER|SA_RESETHAND };
    sigemptyset(&sa.mask);
    sigaction(SIGUSR1, &sa, NULL);
    

    The SA_NODEFER flag together with the empty mask means no signals will be masked; SA_RESETHAND means the signal action will be reset to SIG_DFL.

    In addition, as the other answers said, you shouldn't be calling printf from within the signal handler. The Linux signal safety man page says which functions can be called. sigaction, signal, and pause are OK. You can use write to write strings instead of printf.