Search code examples
csignalsposixkillinterprocess

Ping-pong using kill and flags in POSIX


I'm trying to implement interprocess communication by using POSIX signals in C, especially I'm writing Ping-Pong problem. So here's my source code:

#define CHILD 0
#define PARENT 1

int flag[2];

void handler(int sig) {
    if (sig == SIGUSR1) {
        flag[PARENT] = 1;
    } else {
        flag[CHILD] = 1;
    }   
    return;
}

void child_process() {
    while (1) {
        printf("Ping!\n");
        kill(getppid(), SIGUSR2);
        while (flag[PARENT] == 0) { }
    }
    return;
}

void parent_process(pid_t t) {
    while (1) {
        //kill(t, SIGUSR1);
        while (flag[CHILD] == 0) { }
        printf("Pong!\n");

        kill(t, SIGUSR1);
    }
    return;
}

void setup() {
    flag[CHILD] = 0;
    flag[PARENT] = 0;
    signal(SIGUSR1, handler);
    signal(SIGUSR2, handler);
}

int main(int argc, char* argv[]) {  
    setup();
    pid_t t = fork();
    if (t == 0) {
        child_process();
    } else {
        parent_process(t);
    }
    return 0;
}

My program is not working properly, because sometimes I get "Pong!" "Pong!" "Pong!" or "Ping!" "Ping!" output. What's the problem?

And one more question, is my way of handling signals correct? Or there are more advanced ways to do it?


Solution

  • (1) Parent and child do not share the same memory. flag[CHILD] and flag[PARENT] will never know about each other because they are different copies in different processes.

    (2) Yes, pretty much everything about your signal handling is wrong for what you are trying to do. You are trying to synchronize the signals so you need to use a mechanism that actually synchronizes them e.g. sigsuspend.

    #define _POSIX_C_SOURCE 1
    #include <stdio.h>
    #include <unistd.h>
    #include <signal.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <errno.h>
    
    #define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0)
    
    void sig_hand(int sig) {}
    
    sigset_t saveMask, blockMask;
    
    void child_process()
    {
        int x = 0;
    
        while(x < 10)
        {
            if (sigsuspend(&saveMask) == -1 && errno != EINTR)
                errExit("sigsuspend");
    
            printf("Pong %d!\n", ++x);
    
            kill(getppid(), SIGUSR1);
        }
    
        return ;
    }
    
    void parent_process(pid_t pid)
    {
        int y = 0;
    
        while (y < 10)
        {
            printf("Ping %d!\n", ++y);
    
            kill(pid, SIGUSR1);
    
            if (sigsuspend(&saveMask) == -1 && errno != EINTR)
                errExit("sigsuspend");
        }
    
        return ;
    }
    
    
    int main(int argc, char* argv[])
    {
        //block SIGUSR1 in parent & child until ready to process it
        sigemptyset(&blockMask);
        sigaddset(&blockMask, SIGUSR1);
    
        if (sigprocmask(SIG_BLOCK, &blockMask, &saveMask) == -1)
            errExit("sigprocmask");
    
        //set up signal handler for parent & child
        struct sigaction sa;
        sigemptyset(&sa.sa_mask);
        sa.sa_flags = 0;
        sa.sa_handler = sig_hand;
    
        if (sigaction(SIGUSR1, &sa, NULL) == -1)
            errExit("sigaction");
    
        pid_t pid = fork();
    
        if (pid == 0)
            child_process();
        else
            parent_process(pid);
    
        return 0;
    }