父母和孩子通过信号故障同步

时间:2013-04-18 07:08:47

标签: c linux synchronization signals

所以,我的任务是以这种方式同步父母和他的两个孩子:  一个孩子向父母发送SIGUSR2信号,然后阻止等待父母的信息。 同步由全局标志实现,因此父等待任何flag_ch变为1(它发生在子节点发送SIGUSR2时),然后向该子节点发送信号SIGUSR1,并且子节点恢复(导致全局flag_p变为1)

问题是父母只接收来自一个孩子的信号,然后阻止等待第二个孩子的信号,但它们没有出现 。 任何想法?..

#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 个答案:

答案 0 :(得分:0)

我的猜测是它在一些全局变量声明中缺少volatile。例如,flag_p不是volatile表示循环

while (flag_p == 0) { }

可以永远运行:GCC可能编译它只将一个全局变量加载到寄存器中,然后循环直到该寄存器非零(从不发生)。

保守的近似是你应该volatile在信号处理程序中读取或写入的所有可变变量。

编辑:我能想到的另一个问题来源是信号不是累积的:父进程没有SIGUSR2挂起,或者它有一个。如果两个孩子同时将它发送到父进程,据我所知,只有一个可能会被传递。

编辑:我认为“更好”的解决方案(更灵活,更便携)将是:不要使用信号,而是使用管道。在父项和每个子项之间创建一个管道,子项在完成后在管道上发送一个字符“X”。父母等待select();或者如果它只是想等到两个孩子都准备好了,它可以从一个管道中读取'X'字符然后在另一个管道中读取,在两种情况下都会阻塞(顺序无关紧要)。