【问题标题】:why my first child process does not receive signal from his parent为什么我的第一个子进程没有收到他父母的信号
【发布时间】:2017-12-15 10:46:25
【问题描述】:

这个程序是用 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;
}

你能解释一下为什么吗?

【问题讨论】:

  • 在我的机器上工作。 “SIG HANDLER 通过进程 21265 收到 SIGUSR1 -> Envoie du signal SIGUSR1 au fils 21266”,在信号处理程序中使用 printf 是未定义的行为。
  • 你没有给第一个孩子发送任何信号
  • @Stargateur “Envoie du signal”消息仅表示父级尝试发送信号,而不是接收到。

标签: c linux signals fork


【解决方案1】:

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

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

  • 在信号处理程序中调用printf 通常是不安全的。但是,如果程序在信号到达时正在休眠sigsuspend,那么就可以了。使用sigprocmask 确保SIGUSR1 仅在sigsuspend 调用期间可交付。 (您可以在 Linux signal-safety(7) 手册页中找到可以在信号处理程序中安全调用的函数列表。)

  • 与其对共享内存段做聪明的事,父母应该只保存fork返回给它的子PID。

  • 您应该使用sigaction,而不是signal

  • 您没有检查任何错误。

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

【讨论】:

  • 感谢您的回答,它对我们帮助很大,并提供了很好的研究轴。但是信号有什么问题?因为它做到了我想要的,不多不少。
  • @stotchalex, signal(2) 系统调用已弃用。如果您使用signal(2) 系统调用而不是sigaction(2),则如果它们以快速顺序到达,您可能会丢失一些信号。它做你想做的事(这一次),但不要试图比人们试图帮助你的人更精通。系统调用被保留以使旧的遗留代码工作....但这并不意味着将它用于新的开发是好的。但无论如何,这是你的代码,不是我们的。
  • @zwol,使用共享内存段来沟通父母和孩子可能不是世界上最聪明的事情......但它不是一个错误......不要枚举根本不是的错误,那是不礼貌的。此外,不推荐使用signal(2),但不是错误。检查错误并不聪明,但同样,这不是一个错误,是一个坏习惯。你列举了很多根本不是这样的错误......对不起,但正如你所说,我必须回复。
  • @LuisColorado 所有这些都错误。不检查错误可能是 C 新手犯的第 1 个错误,并且肯定是无法回答“为什么我的程序不能工作?”的最常见原因。问题在这里。使用signal 会使程序的行为在操作系统之间变得不可靠。并且使用共享内存段将 PID 从子级传输到父级会引入竞争条件;修复了所有其他错误后,父母本可以在孩子 1 知道它的 PID 是什么之前尝试向它发出信号。
  • @stotchalex 与signal 函数一起安装的信号处理程序具有不可预测的行为,具体取决于您拥有哪个版本的Unix 实现。它们可能会或可能不会“中断”系统调用,它们可能会或可能不会在处理程序执行期间阻止进一步的信号,并且它们可能会或可能不会在触发一次后重置为默认行为。 sigaction 让您可以根据需要控制上述所有内容。这对这个程序来说不是很重要,但你应该养成专门使用sigaction 的习惯,这样你就可以在重要的时候进行控制。
【解决方案2】:

嗯,父进程直到第一个子进程结束后才发送任何信号(因此它没有机会代表它采取行动)。它只是创建一个子进程,在数组pidt[0] 中注册它的pid(这个数组在与其子进程共享的内存段中)并为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,所以你会在它之后立即得到提示)

注意

孩子对父母的最终杀戮可以使父母正在使用的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);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-12-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-24
    • 1970-01-01
    • 2019-07-23
    相关资源
    最近更新 更多