信号处理问题

时间:2012-01-01 14:51:36

标签: c unix posix signals

在叉号调用之后,我有一个父亲必须向他的孩子发送sigusr1或sigusr2(基于'cod'变量的值)。在接受sigusr1或sigusr2之前,孩子必须安装适当的处理程序。为了这样做,我暂停父亲等待孩子发出信号告诉他已完成处理程序安装。父亲由sigusr1发出信号,并且在fork调用之前安装了此信号的处理程序。然而,看起来父亲不能从暂停中回来让我觉得他实际上从不打电话给sigusr1处理程序。

[...]

    typedef enum{FALSE, TRUE} boolean;

    boolean sigusr1setted = FALSE;
    boolean sigusr2setted = FALSE;


    void
    sigusr1_handler0(int signo){
             return;
    }

    void
    sigusr1_handler(int signo){
            sigusr1setted = TRUE;
    }

    void
    sigusr2_handler(int signo){
            sigusr2setted = TRUE;
    }  

    int main(int argc, char *argv[]){
         [...]

         if(signal(SIGUSR1, sigusr1_handler0) == SIG_ERR){
            perror("signal 0 error");
            exit(EXIT_FAILURE); 
         }

         pid = fork();
                    if (pid == 0){
                        if(signal(SIGUSR1, sigusr1_handler) == SIG_ERR){
                            perror("signal 1 error");
                            exit(EXIT_FAILURE);
                        }

                        if(signal(SIGUSR2, sigusr2_handler) == SIG_ERR){
                            perror("signal 2 error");
                            exit(EXIT_FAILURE);         
                        }

                        kill(SIGUSR1, getppid()); // wake up parent by signaling him with sigusr1

                        // Wait for the parent to send the signals...
                        pause();

                        if(sigusr1setted){
                            if(execl("Prog1", "Prog1", (char*)0) < 0){
                                perror("exec P1 error");
                                exit(EXIT_FAILURE);
                            }
                        }

                        if(sigusr2setted){
                            if(execl("Prog2", "Prog2", (char*)0) < 0){
                                perror("exec P2 error");
                                exit(EXIT_FAILURE);
                            }
                        }

                        // Should'nt reach this point : something went wrong...
                        exit(EXIT_FAILURE);

                    }else if (pid > 0){
                        // The father must wake only after the child has done with the handlers installation

                        pause(); 

                        // Never reaches this point ... 
                        if (cod == 1)
                            kill(SIGUSR1, pid);
                        else 
                            kill(SIGUSR2, pid);

                        // Wait for the child to complete..
                        if(wait(NULL) == -1){
                            perror("wait 2 error"); 
                            exit(EXIT_FAILURE);             
                        }

                             [...]

                    }else{
                        perror("fork 2 error");
                        exit(EXIT_FAILURE);
                    }
         [...]

         exit(EXIT_SUCCESS);
    }

1 个答案:

答案 0 :(得分:0)

从评论中汇总一个看似合理的答案 - 所以这是社区Wiki从一开始就是如此。 (如果奥利提供了答案,那么就投票而不是这个!)

Oli Charlesworth给出了问题的核心内容:

  • 我怀疑你的情况与你的预期相反。在父母到达pause()之前,孩子已将SIGUSR1发送给父母。

ouah准确地注意到了:

  • 信号处理程序和非处理程序代码(您的布尔对象)之间共享的对象必须具有volatile sig_atomic_t类型,否则代码未定义。

也就是说,POSIX允许比标准C更多的松弛,因为它可以在信号处理程序中完成。我们可能还会注意到C99提供了<stdbool.h>来定义bool类型。

原始海报评论说:

  

我不知道如何确保父母在pause()调用时不先在孩子中使用sleep()(不保证任何内容)。有什么想法吗?

建议:使用usleep()(μ-sleep,或以微秒为单位睡眠)或nanosleep()(以纳秒为单位睡眠)?

或使用其他同步机制,例如:

  1. 父进程创建FIFO;
  2. 叉();
  3. child打开FIFO进行写入(阻塞直到有读者);
  4. parent打开FIFO进行读取(阻塞直到有作者);
  5. 当因为open()调用返回而被解除阻塞时,两个进程都只是关闭FIFO;
  6. 父级删除FIFO。
  7. 请注意,两个进程之间没有通过FIFO进行数据通信;代码只是依赖于内核来阻止进程,直到有一个读者和一个编写器,所以两个进程都准备好了。

    另一种可能性是,父进程可以尝试if (siguser1setted == FALSE) pause();来减少竞争条件的窗口。但是,它只会减少窗口;它不能保证不会发生竞争条件。也就是说,墨菲定律适用,信号可以在测试完成和pause()执行的时间之间到达。

    所有这些都表明信号不是一个非常好的IPC机制。它们可以用于IPC,但它们实际上很少用于同步。

    顺便说一句,没有必要测试任何exec*()函数族的返回值。如果系统调用返回,则失败。

    提问者再次问道:

      

    使用进程间共享的POSIX信号量不是更好吗?

    信号量肯定是同步这两个进程的另一种有效机制。因为我当然必须查看信号量的手册页,而我记得如何在不看的情况下使用FIFO,我不确定我是否真的使用它们,但创建和删除FIFO有其自身的一组问题所以不清楚它是否“更好”(或“更糟糕”);只是不同。 FIFO为mkfifo()open()close()unlink()sem_open()(或sem_init()),sem_post(),{{1}信号量可以{},sem_wait(),也许sem_close()(或sem_unlink())。您可能想要考虑使用sem_destroy()注册“FIFO删除”或“信号量清除”功能,以确保在尽可能多的情况下销毁FIFO或信号量。但是,对于测试程序来说,这可能是OTT。