0

I'm trying to create multiple child processes where they send an alarm every few seconds and print a message to standard output. I don't want to use a infinite while loop and I'm trying to basically create a recursion with the signal handler of SIGALRM with alarm(); pause(); every time and I'm having issues with it. it's not printing the proper messages.

This is the code running in child.c

void handle_sigalarm(int sig){
    now = 0;
    time(&now);
    printf("[CHILD] | [TIME = %ld] | [PID = %d] | [GATE = %d]\n", (long int)(now - start), getpid(), GATE);
    alarm(2);
    pause();
}


int main(int argc, char **argv){ 

    signal_handling(); //this is a function handling all signals, it's not included here

    printf("[CHILD] | [TIME = %d] | [PID = %d] | [GATE = %d]\n", 0, getpid(), GATE);

    time(&start);

    alarm(2);
    pause();


    // while(1){
    //     pause();
    // }

    return 0;

}

It works with an infinite while loop but not like this.

The code for the signal handling function:

void signal_handling(){
    struct sigaction sa1;
    struct sigaction sa2;
    struct sigaction sa3;
    struct sigaction sal;

    sa1.sa_handler = handle_sigterm;
    sa2.sa_handler = handle_sigusr1;
    sa3.sa_handler = handle_sigusr2;
    sigemptyset(&sal.sa_mask);
    sal.sa_handler = handle_sigalarm;
    sal.sa_flags = SA_RESTART;

    sigaction(SIGTERM, &sa1, NULL);
    sigaction(SIGUSR1, &sa2, NULL);
    sigaction(SIGUSR2, &sa3, NULL);
    sigaction(SIGALRM, &sal, NULL);
}
  • 1
    Perhaps unrelated, but your code invokes undefined behaviour. `printf(3)` is aysnc signal-unsafe. – Harith Mar 29 '23 at 13:03
  • 1
    Please show the code that sets up the SIGALRM handler. Do you set the SA_NODEFER flag? – Ian Abbott Mar 29 '23 at 13:42
  • 2
    It seems to me a very bad idea for a signal handler to `pause()`. – John Bollinger Mar 29 '23 at 13:49
  • Why *not* use an infinite loop when the objective is for the program to repeat the same behavior indefinitely? – John Bollinger Mar 29 '23 at 13:51
  • 1
    See [How to avoid using `printf()` in a signal handler](https://stackoverflow.com/q/16891019/15168) for more details on why `printf()` is not a good idea in a signal handler. – Jonathan Leffler Mar 29 '23 at 13:56
  • 1
    I anticipate that it would be possible to make your recursion-analog approach work to some extent, but it would have exactly the same problem as an unbounded recursion: limited stack space. And signal handlers run with their own, usually small, stack space, so that would probably bite you sooner rather than later. – John Bollinger Mar 29 '23 at 13:59
  • @JohnBollinger Indeed, if the `SIGALRM` action set-up is changed to set the `SA_NODEFER` flag, and the `printf` call in `handle_sigalarm` is changed to print the address of the `sig` parameter, you can clearly see the address of the `sig` parameter decreasing with each recursive entry to `handle_sigalarm`. – Ian Abbott Mar 29 '23 at 14:20

1 Answers1

3

Note that you never finish handling the first signal because your signal handler doesn't return timely because of the pause(). You are probably going to have to use a loop (maybe, or maybe not, an infinite loop) to get repetition.

When the first signal arrives, your handler is called. Your function illicitly uses printf() to record this. It sets a new alarm (which is valid), and then pauses. That means that the signal handler hasn't returned, and signals blocked by the use of the signal handler remain blocked.

You need to return from the signal handler to unblock the signals.

See the POSIX specification of sigaction() for details, or your system's manual page for it.

POSIX says:

When a signal is caught by a signal-catching function installed by sigaction(), a new signal mask is calculated and installed for the duration of the signal-catching function (or until a call to either sigprocmask() or sigsuspend() is made). This mask is formed by taking the union of the current signal mask and the value of the sa_mask for the signal being delivered, and unless SA_NODEFER or SA_RESETHAND is set, then including the signal being delivered. If and when the user's signal handler returns normally, the original signal mask is restored.

So, further alarm signals are blocked until your handler returns unless you've specified SA_RESETHAND (which would not help you — it would set the signal handler back to SIG_DFL) or SA_NODEFER.

If you used plain signal(), you end up with lots of system-specific behaviour — POSIX allows a lot of different behaviours because existing systems exhibited widely divergent behaviour when POSIX was standardized. You'll need to read your system's manual page on signal() very carefully.


Undefined behaviour

You say your code for setting the signal handling is:

void signal_handling(){
    struct sigaction sa1;
    struct sigaction sa2;
    struct sigaction sa3;
    struct sigaction sal;

    sa1.sa_handler = handle_sigterm;
    sa2.sa_handler = handle_sigusr1;
    sa3.sa_handler = handle_sigusr2;
    sigemptyset(&sal.sa_mask);
    sal.sa_handler = handle_sigalarm;
    sal.sa_flags = SA_RESTART;

    sigaction(SIGTERM, &sa1, NULL);
    sigaction(SIGUSR1, &sa2, NULL);
    sigaction(SIGUSR2, &sa3, NULL);
    sigaction(SIGALRM, &sal, NULL);
}

This code invokes undefined behaviour because the structures are not properly initialized. You neither set the sa_mask values nor the sa_flags for sa1, sa2 or sa3. That means you've no idea what will happen if SIGTERM, SIGUSR1 or SIGUSR2 arrives.

I suggest using this notation to initialize the structures fully:

struct sigaction sa1 = { 0 };

Tangential observation

I also note a weirdness on macOS that the sig*set() functions are implemented as macros with the form (…change bits…, 0), which then generates compiler warnings about 'unused value':

…/signal.h:120:44: error: right-hand operand of comma expression has no effect [-Werror=unused-value]
  120 | #define sigemptyset(set)        (*(set) = 0, 0)

It's a nuisance — either test the result or explicitly use a (void) cast. I use the GCC options:

gcc -g -O3 -std=c18 -pedantic -Wall -Wextra -Werror -Wshadow -Wmissing-prototypes \
    -Wpointer-arith -Wold-style-definition -Wcast-qual -Wstrict-prototypes \
    …
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278