Search code examples
cprintfforkexecl

Handle exactly all signals of type SIGUSR1 that come to parent process. C


I want to write a program which will create N children, using fork() function. Each child will wait from 0 to 3 seconds and then it'll send it's parent exactly one signal SIGUSR1. Parent handles all of these signals.

The problem is that my program is not always handling all signals from it's children. How to repair it?

Second question: I know that I shouldn't use printf inside handler because something bad might happen. How can I replace this instruction?

main.c

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

void error(char *s){
    printf("%s\n",s);
    perror("Program execution failed.");
    exit(EXIT_FAILURE);
}

volatile int N=8; //final number of children
volatile int k=0; //number of received SIGUSR1 signals

void childRequestHandler(int signo, siginfo_t* info, void* context){
    k++;
    printf("%d,Father received request from child: %d\n",k,info->si_pid);

}

int main() {
    struct sigaction act;
    sigemptyset(&act.sa_mask);
    act.sa_flags = SA_SIGINFO;
    act.sa_sigaction = childRequestHandler;
    if(sigaction(SIGUSR1,&act,NULL) == -1) printf("ERROR OCCURED");

    for(int i=0;i<N;i++){
        pid_t pid = fork();
        if(pid == 0) {
            execl("./child", "./child", NULL);
            error("Fork error happened\n");
        }
    }

    while (1){
        sleep(1);
    }

}

child.c

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



int main() {
    time_t t;
    srand((unsigned int) getpid()+ time(NULL));


    int length_of_sleeping = rand() % 4 ;
    printf("I live: %d, sleeps %ds \n",getpid(),length_of_sleeping);
    sleep(length_of_sleeping);
    kill(getppid(),SIGUSR1);

    while(1){

    }

}

output:

I live: 4195, sleeps 3s 
I live: 4196, sleeps 3s 
I live: 4197, sleeps 1s 
I live: 4202, sleeps 3s 
I live: 4198, sleeps 0s 
I live: 4201, sleeps 2s 
I live: 4199, sleeps 0s 
I live: 4200, sleeps 3s 
1,Father received request from child: 4198
2,Father received request from child: 4197
3,Father received request from child: 4201
4,Father received request from child: 4195

Solution

  • When the signal handler is executing, the signal is blocked. All SIGUSR1 signals received during that time will not be noticed.

    You could use the SA_NODEFER flag when establishing the signal, see the sigaction manual page for information about the flag.

    However, if you use SA_NODEFER, don't use printf! It's not an async-signal-safe function. See e.g. this manual page about signal safety for more information.


    From the POSIX sigaction reference:

    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.... 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.

    [Emphasis mine]