user11210665
user11210665

Reputation:

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?

Upvotes: 4

Views: 8014

Answers (2)

Petr Skocik
Petr Skocik

Reputation: 60056

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; 
}

Upvotes: 5

r3mus n0x
r3mus n0x

Reputation: 6144

You have two issues:

  1. The child process is getting a signal before it calls pause().
  2. SIGINT by default would kill a process so printf will never be executed.

Try this:

void handler(int signum)
{
    //nothing here
}

int main()
{
    pid_t c = fork();
    if (c == 0) {
        signal(SIGINT, handler);
        pause();
        printf("signal was given");
    }
    if (c > 0) {
        sleep(1); // <-- give the child process some time to pause()
        kill(c, SIGINT);
    }

    return 0;
}

Upvotes: 0

Related Questions