代码之家  ›  专栏  ›  技术社区  ›  stotch alex

为什么我的第一个子进程没有收到来自其父进程的信号

  •  0
  • stotch alex  · 技术社区  · 8 年前

    这个程序是用C编写并在ubuntu上编译的。 我的脚本创建了两个子脚本。 第一个在静态变量中记录他的pid。 创建第二个子级并向父级发送信号。

    家长接收信号,并在该回合向第一个孩子发送信号。 但是第一个孩子没有收到这个信号。从未调用函数sig\U handlerr。

    # include <stdio.h>
    # include <stdlib.h>
    # include <unistd.h>
    # include <sys/types.h>
    # include <sys/wait.h>
    # include <sys/stat.h>
    # include <fcntl.h>
    # include <time.h>
    # include <signal.h>
    # include <sys/mman.h>
    
    static pid_t* pidt;
    
    // handler signal père
    void sig_handler(int signo)
    {   
      if (signo == SIGUSR1) {
        printf("SIG HANDLER received SIGUSR1 by process %d\n", getpid());
        printf("-> Envoie du signal SIGUSR1 au fils %d\n", pidt[0]);
        // le père envoie un signal au premier fils
        kill(pidt[0], SIGUSR1);
      }
    
    }
    // handler signal fils
    void sig_handlerr(int signo)
    {   
      if (signo == SIGUSR1) {
        printf("SIG HANDLER2 received SIGUSR1 by process %d\n", getpid());
      }
    
    }
    
    int main(int argc, char* argv[]) {
    
        printf("pid du processus père %d\n", getpid());
    
        pidt = mmap(NULL, sizeof(pid_t), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    
        if (fork()==0) {    
            printf("1 processus fils %d\n", getpid());
            pidt[0]=getpid();
            printf("pid enregistré= %d\n", pidt[0]);
            signal(SIGUSR1, sig_handlerr);
            exit(0);
        }
        wait(NULL); 
    
        signal(SIGUSR1, sig_handler);
    
        if (fork()==0) {
            printf("2 processus fils %d", getpid());
    
            // il envoie un signal SIGUSR1 au processsu père
            printf("-> envoie SIGUSR1 au père %d\n", getppid());
            kill(getppid(), SIGUSR1); // envoie un signal SIGUSR1 au processus père
            exit(0);
        }
        wait(NULL);
    
        printf("\n\n------ Fin du programme -------");
        return EXIT_SUCCESS;
    }
    

    你能解释一下为什么吗?

    2 回复  |  直到 8 年前
        1
  •  6
  •   zwol    8 年前

    您有很多bug,其中最重要的是:

    • 调用后第一个子进程立即退出 signal ,并且父进程在启动第二个进程之前等待它。这意味着,当父母开始向第一个孩子发送信号时,第一个孩子已经不在那里接收信号了。使用 sigsuspend 让父母和第一个孩子睡觉直到信号到达,然后打电话 wait 只有在完成所有其他工作后,才能在父级中。

    • 打电话通常不安全 printf 信号处理器内部。但是,如果程序正在休眠 SIGSSUSPEND 当信号到达时,一切正常。使用 sigprocmask 确保 SIGUSR1 仅在 SIGSSUSPEND 电话。(您可以在Linux的信号处理程序中找到可以安全调用的函数列表 signal-safety(7) 手册页。)

    • 与使用共享内存段进行巧妙的操作不同,父级应该只保存返回给它的子PID fork .

    • 您应该使用 sigaction 信号 .

    • 您没有检查任何错误。

    • 您没有检查子进程是否成功退出。

        2
  •  0
  •   Community Mohan Dere    6 年前

    嗯,父进程直到第一个子进程结束后才发送任何信号(因此它没有机会代表它)。它只创建一个子进程,在数组中注册其pid pidt[0] (此数组位于与其子级共享的内存段中)并为 SIGUSR1 信号(没有用,因为 exit() 就在那之后(它几乎没有时间接收信号,但读得更多,就不行了)。然后,家长 wait() 第一个孩子的死 (这是错误的)。。。发生了什么。。。所以之后只有一个过程。

    然后,父进程安装一个信号处理程序并创建第二个子进程。它是 kill() 由子进程执行,您可以从信号处理程序执行后的输出中看到这一点。由于没有第一个孩子还活着,因此没有机会接收到信号,也没有机会看到预期的输出(您不会说预期会有此输出,但我猜:))

    $ a.out
    pid du processus père 3095
    1 processus fils 3097
    pid enregistré= 3097
    pid du processus père 3095
    2 processus fils 3098-> envoie SIGUSR1 au père 3095
    pid du processus père 3095
    SIG HANDLER received SIGUSR1 by process 3095
    -> Envoie du signal SIGUSR1 au fils 3097
    
    
    ------ Fin du programme -------$ _
    

    (看,没有 \n 在最后一条消息的末尾,因此您会立即收到提示)

    注意事项

    最后杀死孩子的父母可以 等待() 父级正在使用的系统调用,用于等待第二个子级被中断(您不检查它的返回值),因此,最终 printf() 可以在第二个子级实际退出之前执行(子级除了 退出() 因此,在孩子最终退出后,什么都看不到,但你也必须检查这一点)。

    关于父信号处理程序中发送的信号。。。。三思:您已经启动了第二个子系统(即向父系统发送第一个信号的子系统) 在第一个孩子 exit(2) 预计起飞时间 ,因此它不可能接收到信号。您没有检查 kill(2) syscall,因为它会告诉您目标进程不存在。

    以下代码修复损坏的零件。。。首先,父母不能等待孩子死亡。在安装其信号处理程序后,子级必须处于活动状态并等待信号(这就是 pause(2) 是否,等待信号到达):

    # include <stdio.h>
    # include <stdlib.h>
    # include <unistd.h>
    # include <sys/types.h>
    # include <sys/wait.h>
    # include <sys/stat.h>
    # include <fcntl.h>
    # include <time.h>
    # include <signal.h>
    # include <sys/mman.h>
    
    static pid_t* pidt;
    
    // handler signal père
    void sig_handler(int signo)
    {   
      if (signo == SIGUSR1) {
        printf("SIG HANDLER received SIGUSR1 by process %d\n", getpid());
        printf("-> Envoie du signal SIGUSR1 au fils %d\n", pidt[0]);
        // le père envoie un signal au premier fils
        kill(pidt[0], SIGUSR1);
      }
    
    }
    // handler signal fils
    void sig_handlerr(int signo)
    {   
      if (signo == SIGUSR1) {
        printf("SIG HANDLER2 received SIGUSR1 by process %d\n", getpid());
      }
    
    }
    
    int main(int argc, char* argv[]) {
    
        printf("pid du processus père %d\n", getpid());
    
        pidt = mmap(NULL, sizeof(pid_t), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    
        if (fork()==0) {    
            printf("1 processus fils %d\n", getpid());
            pidt[0]=getpid();
            printf("pid enregistré= %d\n", pidt[0]);
            signal(SIGUSR1, sig_handlerr);
            pause();
            exit(0);
        }
    
        signal(SIGUSR1, sig_handler);
    
        if (fork()==0) {
            printf("2 processus fils %d", getpid());
    
            // il envoie un signal SIGUSR1 au processsu père
            printf("-> envoie SIGUSR1 au père %d\n", getppid());
            kill(getppid(), SIGUSR1); // envoie un signal SIGUSR1 au processus père
            exit(0);
        }
        wait(NULL);
    
        printf("\n\n------ Fin du programme -------");
        return EXIT_SUCCESS;
    }
    

    您将获得以下输出:

    $ a.out
    pid du processus père 3301
    2 processus fils 3304-> envoie SIGUSR1 au père 3301
    pid du processus père 3301
    1 processus fils 3303
    pid enregistré= 3303
    SIG HANDLER2 received SIGUSR1 by process 3303
    pid du processus père 3301
    SIG HANDLER received SIGUSR1 by process 3301
    -> Envoie du signal SIGUSR1 au fils 3303
    
    
    ------ Fin du programme -------$ _
    

    (同样,在最后一条消息之后出现提示:))

    您的代码与我的代码之间的差异如下,请注意这些差异:

    $ diff -c a-old.c a-new.c
    *** a-old.c 2017-12-18 11:54:51.000000000 +0100
    --- a-new.c 2017-12-18 11:43:21.000000000 +0100
    ***************
    *** 42,50 ****
              pidt[0]=getpid();
              printf("pid enregistré= %d\n", pidt[0]);
              signal(SIGUSR1, sig_handlerr);
              exit(0);
          }
    -     wait(NULL); 
      
          signal(SIGUSR1, sig_handler);
      
    --- 42,50 ----
              pidt[0]=getpid();
              printf("pid enregistré= %d\n", pidt[0]);
              signal(SIGUSR1, sig_handlerr);
    +         pause();  /* wait for a signal to arrive */
              exit(0);
          }
      
          signal(SIGUSR1, sig_handler);