1
votes

The parent process creates a Child. Then I want them both to print to STDOUT alternatively. For this I'm using pause() and kill(pid,SIGUSR1). I've declared a dummy handler for SIGUSR1.

It compiles fine. Runs fine until it gets stuck. And it gets stuck at different occasions.

I think semaphores will be a way better appraoch to this problem, but I want to use pause-kill to increse my understanding of signals.

I think whats happening is both parent and child get paused because one of them gets descheduled just after the kill() and before pause(). I need to find a way to overcome this.

The expected output is both printing till 4999.

void action(int signum){}

int main ()
{
  int i = 0, j = 0; 
  pid_t ret;
  int status;

  signal(SIGUSR1,action);

  ret = fork ();
  if (ret == 0)
    {
      for (i = 0; i < 5000; i++){
        printf ("Child: %d\n", i);
        kill(getppid(),SIGUSR1);
        pause();
      }

      printf ("Child ends\n");
    }
  else
    { 
      for (j = 0; j < 5000; j++){
          printf ("Parent: %d\n", j);
          kill(ret,SIGUSR1);
          pause();
      }
    }
}

Some current output I'm getting are-

.... Child: 3844 Parent resumes. Parent: 3844 //after this both hang(or pause I think)

.... Child: 44 Parent resumes. Parent: 44 //after this both hang(or pause I think)

.... Child: 574 Parent resumes. Parent: 574 //after this both hang(or pause I think)

1

1 Answers

2
votes

There is a window of time between kill and pause during which a signal can be delivered into the signal handler. In this case pause blocks the process forever.

A fix would be to block SIGUSR1 first and then use sigsuspend (ยง Notes is instructive) to wait for the SIGUSR1 in atomic fashion. This way the signal can only be delivered while in the call to sigsuspend:

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

void action(int signum) {}

void perror2(char const* msg) {
    perror(msg);
    exit(EXIT_FAILURE);
}

int main() {
    int i = 0, j = 0;
    pid_t ret;
    sigset_t sigset;

    sigemptyset(&sigset);
    sigaddset(&sigset, SIGUSR1);
    sigprocmask(SIG_BLOCK, &sigset, NULL);
    sigemptyset(&sigset);
    signal(SIGUSR1, action);

    ret = fork();
    if(ret == 0) {
        for(i = 0; i < 5000; i++){
            printf("Child: %d\n", i);
            kill(getppid(), SIGUSR1);
            if(-1 != sigsuspend(&sigset) || EINTR != errno)
                perror2("sigsuspend");
        }
        printf("Child ends\n");
    }
    else {
        for(j = 0; j < 5000; j++){
            printf("Parent: %d\n", j);
            kill(ret,SIGUSR1);
            if(-1 != sigsuspend(&sigset) || EINTR != errno)
                perror2("sigsuspend");
        }
    }
    return 0;
}

An alternative simple fix would be to use pipes for communication between the parent process and its child. Pipes also allow you to pass more than 1 bit of information between the process. E.g.:

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

int const N = 100;

void perror2(char const* msg) {
    perror(msg);
    exit(EXIT_FAILURE);
}

int child(int from_parent, int to_parent) {
    for(int i = N, j; i--;) {
        if(sizeof j != read(from_parent, &j, sizeof j))
            perror2("child read");
        printf("child received %d\n", j);
        if(sizeof i != write(to_parent, &i, sizeof i))
            perror2("child write");
    }
    return 0;
}

int parent(int from_child, int to_child) {
    for(int i, j  = N; j--;) {
        if(sizeof j != write(to_child, &j, sizeof j))
            perror2("parent write");
        if(sizeof i != read(from_child, &i, sizeof i))
            perror2("parent read");
        printf("parent received %d\n", i);
    }
    return 0;
}

int main() {
    int to_child[2], to_parent[2];
    if(pipe(to_child))
        perror2("pipe 1");
    if(pipe(to_parent))
        perror2("pipe 2");

    switch(fork()) {
    case -1:
        perror2("fork");
    case 0:
        return child(to_child[0], to_parent[1]);
    default:
        return parent(to_parent[0], to_child[1]);
    }
}