为什么我的第一个子进程没有收到父母发出的信号

问题描述 投票:0回答:2

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

父母接收信号并在此回合向第一个孩子发送信号。但是第一个孩子没有收到这个信号。从不调用函数sig_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;
}

你可以解释一下为什么好吗?

c linux signals fork
2个回答
6
投票

你有很多错误,其中最重要的是:

  • 调用signal后,您的第一个子进程立即退出,并且父进程在开始第二个进程之前等待它。这意味着,当父母到处向第一个孩子发送信号时,第一个孩子不再在那里接收它。使用sigsuspend使父母和第一个孩子睡觉直到信号到达,并且只有在完成所有其他工作后才在父母中调用wait
  • 在信号处理程序中调用printf通常是不安全的。但是,如果程序在信号到达时在sigsuspend中睡觉,那么它就可以了。使用sigprocmask确保SIGUSR1只能在sigsuspend调用期间交付。 (您可以在Linux signal-safety(7)联机帮助页中找到可在信号处理程序中安全调用的函数列表。)
  • 父级应该只保存由fork返回给它的子PID,而不是使用共享内存段做聪明的事情。
  • 你应该使用sigaction,而不是signal
  • 您没有检查是否有任何错误。
  • 您没有检查子进程是否已成功退出。

0
投票

好吧,父进程直到第一个孩子结束后才发出任何信号(所以没有机会代表他们行事)。它只是创建一个子进程,将其pid注册到一个数组pidt[0](在与其子进程共享的内存段中的此数组)并为SIGUSR1信号安装一个信号处理程序(没有用,因为它之后的exit()s(它有几乎没有时间接收信号,但阅读更多,它不能)。然后,父亲wait()s第一个孩子的死(这是错误的)......发生...所以之后只有一个过程。

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

$ 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,所以你得到它之后的提示)

NOTE

孩子最终被父母杀死可以使父母用来等待第二个孩子被中断的wait()系统调用(你没有检查它的返回值),所以,最后的printf()可以在第二个孩子的实际退出之前执行(孩子除了exit()之外什么都不做,所以在孩子最后退出后什么也看不到,但你也要检查一下)。

关于在父信号处理程序中发送的信号....请三思:在第一个孩子有exit(2)ed之后你已经启动了第二个孩子(这是向父母发送第一个信号的孩子),所以它没有可能收到信号。您不检查kill(2)系统调用的结果代码,因为它会告诉您TARGET进程不存在。

以下代码修复损坏的部分......首先,父母不能等待孩子死亡。在安装信号处理程序后,孩子必须活着并等待信号(这是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);
© www.soinside.com 2019 - 2024. All rights reserved.