0
votes

So, my task is to sync parent and his 2 children in this way: one child sends SIGUSR2 signal to parent and then blocks waiting parent msg. The sync is implemented by global flags, so the parent waits for any of the flag_ch become 1 (it happens when child sends SIGUSR2) and then sends signal SIGUSR1 to this child, and child resumes (cause global flag_p becomes 1)

the trouble is that parent receives signals only from one child, and then blocks waiting for second child signals, but they don't appear . any idea?..

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/signalfd.h>
#define LPC 10
pid_t ch[2];

sig_atomic_t flag_ch[2] = {0, 0};
sig_atomic_t flag_p = 0;

int get_ind(pid_t ch_pid) {
    int i;
    for (i = 0; i < 2; ++i) {
        if (ch_pid == ch[i])
            return i;
    }
    return -1;
}

void usr_handler(int signo, siginfo_t* si, void* unused) {
    int ch_index;
    switch(signo) {
        case SIGUSR2:
            ch_index = get_ind(si->si_pid);
            if (ch_index >= 0)
                flag_ch[ch_index] = 1;
            else
                fprintf(stderr, "signal handled not from child pid %d\n", si->si_pid);
            break;
        case SIGUSR1:
            flag_p = 1;
            break;
    }
}

void set_usr_handler(void) {
    struct sigaction sa;
    sa.sa_sigaction = usr_handler;
    sa.sa_flags = SA_SIGINFO;
    sa.sa_restorer = NULL;

    sigemptyset(&sa.sa_mask);

    if (0 != sigaction(SIGUSR1, &sa, NULL))
        abort_prg("signal [SIGUSR1] error");

    if (0 != sigaction(SIGUSR2, &sa, NULL))
        abort_prg("signal [SIGUSR2] error");
}


void child_proc(void) {
    int i;
    for (i = 0; i < LPC; ++i) {
        if (0 != kill(getppid(), SIGUSR2))
            exit(1);
        while (0 == flag_p) { };
            flag_p = 0;
    }
}

int wait_child(void) {
    while (0 == flag_ch[0] && 0 == flag_ch[1]) { };
    if (1 == flag_ch[0]) {
        flag_ch[0] = 0;
        return ch[0];
    }
    flag_ch[1] = 0;
    return ch[1];
}

void parent_proc(void) {
    int i;
    pid_t ch_pid;
    for (i = 0; i < LPC * 2; ++i) {
        ch_pid = wait_child();
        printf("Parent: Received from pid [%d]\n", ch_pid);
        if (0 != kill(ch_pid, SIGUSR1))
            exit(1);
    }
}

int main(int argc, char* argv[]) {
    set_usr_handler();
    int i;
    for (i = 0; i < 2; ++i) {
        pid_t child = fork();
        if (0 > child)
            exit(1);
        if (0 == child) {
            child_proc();
            return 0;
        }
        ch[i] = child;
    }   
    parent_proc();
    return 0;
}
1
did you intentionally write while (0 == flag_p) { }; ? - Infested
@AnishRam no, the value child gets in the father is the child pid. - Infested
'while (0 == flag_p) { };' blocks the child until parent's signal is delievered - NinjaTurtle
but the flag_p are not connected to each other. each process holds its own flag_p - Infested
that's why i use signal SIGUSR1 which parent sends to child to change flag_p value - NinjaTurtle

1 Answers

0
votes

My guess is that it's missing volatile in a few global variable declarations. For example, flag_p not being volatile means that the loop

while (flag_p == 0) { }

can run forever: GCC probably compiles it to load the global variable only once into a register, and then loop until this register is non-zero (which never occurs).

A conservative approximation is that you should make volatile all mutable variables that are read from or written to in a signal handler.

EDIT: another source of problem I can think of is that signals are not cumulative: either the parent process has no SIGUSR2 pending, or it has one. If both children send it to the parent process at the same time, only one might be delivered, as far as I know.

EDIT: I think a "better" solution (more flexible and more portable) would be along the lines of: do not use signals at all, but use pipes. You make one pipe between the parent and each of the children, and the children send a character 'X' over the pipes when they are done. The parent waits with select(); or if it just wants to wait until both children are ready, it can read the 'X' character from one pipe and then the other, blocking in both cases (the order doesn't matter).