2

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?

alk
  • 69,737
  • 10
  • 105
  • 255
Ricky
  • 635
  • 2
  • 5
  • 20
  • This is just a minor detail, but still: while `sig_rcv = (sig_rcv+1)%2` does get the job done, the same can be achieved more elegantly by just `sig_rcv = 1-sig_rcv` or even `sig_rcv ^= 1`. With regard to the actual question, it is a common practice to have a "main loop" of the program which would look something like `while (running) ...` (where `running` is a global variable), and have signal handlers simply change the value of `running`. That way you could check the value of `running` inside a single signal handler instead of using two different handlers for two different signals. – kyrill Mar 10 '19 at 10:35
  • What operating system are you using? – Mark Plotnick Mar 11 '19 at 00:44
  • @MarkPlotnick Ubuntu 18.04 – Ricky Mar 11 '19 at 13:55

3 Answers3

5

Can't we wait for a signal while executing the signal handler written for the same signal?

Whether the code waited "forever" or would receive the same signal again while already processing it, is depending on the (C/OS) implementation.

From the POSIX documentation:

During the time between the generation of a signal and its delivery or acceptance, the signal is said to be "pending".

and

If a subsequent occurrence of a pending signal is generated, it is implementation-defined as to whether the signal is delivered or accepted more than once [...]


And, BTW, calling printf() & friends from a signal handler is unsafe.

alk
  • 69,737
  • 10
  • 105
  • 255
2

More generally than @alk's answer, you can't call any library function from within a signal handler in strictly-conforming C code.

Per 7.1.4 Use of library functions, paragraph 4 of the C standard:

The functions in the standard library are not guaranteed to be reentrant and may modify objects with static or thread storage duration.188

Note footnote 188, for that very paragraph, states:

Thus, a signal handler cannot, in general, call standard library functions.

As @alk states, POSIX allows you to safely call only async-signal-safe functions from within a signal handler.

Absent specific documentation for your platform that states a specific function call is safe to make from within a signal handler, you can't call a function from within a signal handler.

Andrew Henle
  • 32,625
  • 3
  • 24
  • 56
2

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.

Community
  • 1
  • 1
Mark Plotnick
  • 9,598
  • 1
  • 24
  • 40