Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using signals in a child process

I want to create a simple program that uses fork and creates a child process which with the use of pause is waiting. I want this child process to start after it gets a specific signal from father process. Code I've written:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

int main() {
    pid_t c = fork();
    if (c == 0) {
        pause();
        printf("signal was given");
    }
    if (c > 0)
        kill(c, SIGINT);

    return 0;
}

I think kill gives a specific signal to a process with pid c(child) and I thought that pause just waits for a signal to unpause that process. However in this case running this program has no results. I have also tried adding a signal catching function to the child using signal(SIGINT, handler) and creating a handler function that prints the desired result but it is still not working. Any ideas?


1 Answers

If you send SIGINT, whose default disposition is to kill the process, to a process that neither blocks it nor handles it, the process will die.

If you want the signal to interrupt blocking calls like pause(), it needs to have a handler.

But simply installing a handler introduces race conditions:

if (c == 0 ){
    //< if the signal arrives here the child dies
    signal(SIGINT, handler);
    //< if the signal arrives here then nothing happens except the handler is run
    pause(); //< if the handler arrives here then pause gets interrupted
    printf("signal was given\n");
    exit(0);
}

To eliminate the race conditions, you need to

  1. block the signal in the parent so that the child starts with the signal blocked
  2. install the handler in the child
  3. unblock the signal and pause() in one atomic step

To achieve 3. in one step, you need sigsuspend() instead of pause().

#include <stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<signal.h>

void handler(int Sig){}

int main()
{
    sigset_t sigint, oldmask; sigemptyset(&sigint); sigaddset(&sigint, SIGINT);
    sigprocmask(SIG_BLOCK, &sigint, &oldmask);

    pid_t c=fork();
    if(0>c) return perror(0),1;
    if (c==0){
        signal(SIGINT, handler);
        sigdelset(&oldmask,SIGINT); /*in (the unlikely) case the process started with SIGINT blocked*/
        sigsuspend(&oldmask);
        printf("signal was given\n");
        exit(0);
    }
    kill(c,SIGINT);
    wait(0);
    return 0; 
}

Alternatively, you can use sigwait() and drop the need for a handler altogether:

#include <stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<signal.h>

int main()
{
    sigset_t sigint, oldmask; sigemptyset(&sigint); sigaddset(&sigint, SIGINT);
    sigprocmask(SIG_BLOCK, &sigint, &oldmask);

    pid_t c=fork();
    if(0>c) return perror(0),1;
    if (c==0){
        int sig; sigwait(&sigint,&sig);
        printf("signal was given\n");
        exit(0);
    }
    kill(c,SIGINT);
    wait(0);
    return 0; 
}
like image 198
PSkocik Avatar answered Sep 18 '25 10:09

PSkocik