C, Fork and exec process, and sending signals

Basically I have two codes. One of them must print in shell "TIME!" everytime it receives a SIGUSR1 signal. Let's call it exercici.c

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

char buffer[1000];

void captura_signal(int signum){

    if(signum == SIGUSR1){
        sprintf(buffer,"TIME! %d\n",getpid());
        write(1,buffer,strlen(buffer));
    }
}


int main(int argc, char *argv[]){
    signal(SIGUSR1, captura_signal);
    while(1){
        pause();
    }

}

The other one, must create three child process (only one parent and three childs) and execlp exercici.c on each child. Then, every second, the parent have to send a SIGUSR1 to one child, incrementing each second. I'll call it exercici2.c

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


bool ready = 1;

void  sigAlarm(int sig){
    if (sig == SIGALRM){
        ready = 1;
    }
}


int main(int argc, char *argv[]){
    int pid[3];
    signal(SIGALRM,sigAlarm);
    for(int i = 0; i<3;i++){
        if((pid[i]=fork()) == 0){
            execlp("./exercici","exercici",(char*)NULL);
        }
    }
    int j =0;
    while(1){
        if(ready){
            ready = 0;
            kill(pid[j],SIGUSR1);
            j = ((j+1)%3);
            alarm(1);
        }
    }
}

The problem is that when I execute exercici2.c it creates only 2 processes and I am only receiving TIME! of 2 of them. It skips the pid[0] one.

shell image

2 answers

  • answered 2017-10-11 10:21 Abhijit

    It is bit tricky to understand fork() and creation of a child process and which part of code that child process will execute. Here you are calling the while(1) loop from the each child process, that portion of code is not executing in main process but executing inside child process only. Hence ‘ j’ is always 0 and you are actually executing like kill(pid[0],SIGUSR1); ( 3 times).

    It is always better to control child process from parent process. See the modified code below which will work for you.

    int parentPID = 0;
    
    int main(int argc, char *argv[]){
     int pid[3];
     parentPID = getpid();
     signal(SIGALRM,sigAlarm);
     for(int i = 0; i<3;i++){
        if((pid[i]=fork()) == 0){
            printf("child :%d\n",i);
            execlp("./exercici","exercici",(char*)NULL);
        }
     }
     if (parentPID == getpid()){
         sleep(5);/* Let the 3 childs create */
         int j = 0;
         int ready = 1;
         while(ready){
           kill(pid[j],SIGUSR1);
           j = ((j+1)%3);
           ready = j;
           alarm(1);
         }
        }
    

    }

  • answered 2017-10-11 10:21 H.S.

    The problem is occurring because of -

    int j =0;
    while(1){
        if(ready){
            ready = 0;
            kill(pid[j],SIGUSR1);
            j = ((j+1)%3);
            alarm(1);
        }
    }
    

    in parent process.

    Here you are initializing j with 0 and in the while loop kill() send SIGUSR1 to pid[0] in the first iteration.

    Each signal has a "default disposition" -- what a process does by default when it receives that signal.

    From signal(7) man page -

    SIGUSR1 and SIGUSR2 default disposition is - Term, which means terminate the process.

    Signal Value Action Comment

    ───────────────────────────────────────────

    SIGUSR1 30,10,16 Term User-defined signal 1

    SIGUSR2 31,12,17 Term User-defined signal 2

    In your exercici child program, you are changing the disposition of a signal SIGUSR1 and setting the disposition to handler function captura_signal -

     signal(SIGUSR1, captura_signal)
    

    In your case, whats happening is that the child process with pid[0] when forked and before the child process exec exercici and set the SIGUSR1 signal handler, the parent process sent SIGUSR1 to pid[0]. And since the default disposition of SIGUSR1 is to terminate the process, the pid[0] child process is terminate and marked defunct.

    On my system, I can see in the output of ps command -

    ps -eaf | grep exercici

    root 29210 19542 86 20:51 pts/5 00:00:04 ./exercici2 --------> Parent process

    root 29211 29210 0 20:51 pts/5 00:00:00 [exercici2] < defunct > ------> Child process received signal SIGUSR1 before exec'ing exercici

    root 29212 29210 0 20:51 pts/5 00:00:00 exercici --------> Child process successfully exec'ed exercici and changed default disposition of SIGUSR1

    root 29213 29210 0 20:51 pts/5 00:00:00 exercici --------> Child process successfully exec'ed exercici and changed default disposition of SIGUSR1

    Here you can see the first child process with pid 29211 is marked defunct.

    You can solve this problem by various ways. One of them is to simply ignore SIGUSR1 in parent process by adding this statement -

    signal(SIGUSR1,SIG_IGN);
    

    somewhere at the start of parent process forking child processes.

    A child process inherits signal handler from the parent process. So before exec'ing current process image with the new process (exercici) if the child process receives SIGUSR1, it will ignore it.