Search code examples
cunixprocesssignalsfork

How to make synchronization between processes using signals?


The objective is the parent process should make a child one, the parent prints the even numbers between 1 to 100, the child prints the odd ones. This mechanism should be made using signals (the numbers should be in order obviously e.g. parent:0, child:1, parent:2...) I made the following code:

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

int main(){
    pid_t pid;

    pid = fork();

    if (pid == -1){
        return 1;
    }
    
    if (pid == 0){
            
        for(int i=0; i<=100; i++){
            if (i % 2 != 0){
                printf("I am the child: %d\n", i);
            }
        }

    } else {
        kill(pid, SIGSTOP);
        for(int i=0; i<=100; i++){
            if(i % 2 == 0){
                printf("I am the parent: %d\n", i);
                kill(pid, SIGCONT);
            }
        }
        wait(NULL);
    }

    return 0;
}

But the output isn't in the order it should be, the parent prints all of his numbers first, the child follows him. I was wondering if the SIGSTOP and SIGCONT aren't the suitable signals to be used, but there's no other logical solution.

Any suggestion would be helpful. Thank you.


Solution

  • Block on a signal and resume only when that signal is received. Regarding which signal to use, you could use any that can be blocked (any signal excluding SIGKILL, SIGSTOP). I used SIGRTMIN because it is for application-defined purposes.

    #include<stdlib.h>
    #include<stdio.h>
    #include<unistd.h>
    #include<signal.h>
    #include<sys/wait.h>
    
    int main(){
        /* Block SIGRTMIN */
    
        sigset_t sigmask;
    
        sigemptyset(&sigmask);
        sigaddset(&sigmask, SIGRTMIN);
    
        if (sigprocmask(SIG_BLOCK, &sigmask, NULL) != 0)
            exit(1);
        
        int signo;
    
        /* Spawn a new child */
    
        pid_t pid;
    
        pid = fork();
    
        if (pid == -1){
            return 1;
        }
        
        if (pid == 0){
            sigwait(&sigmask, &signo);  // This is to make parent start first.
            for(int i=1; i<=100; i=i+2){
                printf("I am the child: %d\n", i);
    
                kill(getppid(), SIGRTMIN);  // Resume parent
                sigwait(&sigmask, &signo);  // Wait for parent
            }
    
        } else {
            for(int i=0; i<=100; i=i+2){
                printf("I am the parent: %d\n", i);
    
                kill(pid, SIGRTMIN);    // Resume child
                if (i == 100)   // Parent stucks at sigwait after printing 100.
                    break;
                sigwait(&sigmask, &signo);  // Wait for child
            }
            wait(NULL);
        }
    
        return 0;
    }
    

    We blocked SIGRTMIN in the parent but how does it gets blocked in the child?

    According to signal(7) man page.

    A child created via fork(2) inherits a copy of its parent's signal mask; the signal mask is preserved across execve(2).