【问题标题】:Why doesn't child process continue running after receiving signal?为什么子进程收到信号后不继续运行?
【发布时间】:2011-11-24 16:47:11
【问题描述】:

以下是我的代码。父母叉一个孩子。子暂停直到父发送信号给它,然后它继续运行。 我的问题是为什么子进程不继续 在父母向他发送信号之后运行。我错过或误解了什么吗?

#include<stdio.h>
#include<unistd.h>
#include<signal.h>


void 
sigusr1( int pidno )
{
  printf("Catched\n");
}

int 
main()
{
  pid_t pid;

  signal( SIGUSR1, sigusr1 );
  if( (pid = fork()) == 0 ){
    pause();
  printf("Child\n"); 
  }

  kill( pid , SIGUSR1 ); //parent sends signal to child 
  pause();
}

【问题讨论】:

    标签: unix process fork signals


    【解决方案1】:

    试试这个:

    #include<stdio.h>
    #include<unistd.h>
    #include<signal.h>
    
    
    void
    sigusr1( int pidno )
    {
      fprintf(stderr, "Caught\n");
    }
    
    int
    main()
    {
      pid_t pid;
    
      signal( SIGINT, sigusr1 );
      if( (pid = fork()) == 0 ){
        pause();
        fprintf(stderr, "Child\n");
      }
      else
      {
        fprintf(stderr, "Parent\n");
        kill( pid , SIGINT ); //parent sends signal to child
      }
      pause();
      return 0;
    }
    
    1. printf buffers input: 也许它正在被调用,但它没有在你期望的时候显示。一种解决方法是fflush()。更好的解决方法是fprintf (stderr),它一开始就不会缓冲。

    2. 您在父级和子级中都调用了kill()。我添加了一个else 来消除这个问题。

    3. 这是示例输出:

    gcc -Wall -g -o z z.c
    
    ./z
    Parent
    Caught
    Child
    

    【讨论】:

    • 谢谢。我复制了你的代码,但它似乎在我的 mac 上不起作用。
    • 我认为原因是在暂停运行之前发送了信号。
    • #1 错误:如果程序在终端上打印,它将使用行缓冲。 #2 是对的,但我认为这不是问题的全部。
    • @Gilles - 否:打印缓冲。缓冲一般来说很好,但它可能会在多个进程或多个线程中产生意想不到的结果。是的,在原始代码中杀死的地方是主要问题。
    • @Janus Le - "signal()" 设置信号处理程序。 “kill()”向进程发送信号。在“fork()”和随后的“pause()”之前调用信号是很好的。
    【解决方案2】:

    以下是父级中发生的情况:

    1. 叉一个孩子。
    2. 向孩子发送 SIGUSR1。
    3. 等待信号。

    这是孩子身上发生的事情:

    1. 等待信号。
    2. 打印Child
    3. 调用kill(0, SIGUSR1)(0 是孩子中pid 的值)。使用进程 ID 0 调用 kill 会将信号发送到调用 kill 的进程的 process group 中的每个进程。
    4. 等待信号。

    您的程序有几种可能的行为,具体取决于父系统调用和子系统调用的执行顺序。根据您的操作系统的确切版本、各种内核参数的微调、系统的负载程度以及随机机会,如果您多次运行程序或在调试器。

    如果父母的启动速度比孩子快,您可能会看到:

    1. 父母向孩子发送 SIGUSR1。
    2. 孩子收到 SIGUSR1 并打印 Catched
    3. 孩子打电话pause
    4. 家长致电pause

    有了这个执行顺序,父母和孩子最终都会永远等待(这是一个deadlock)。

    如果孩子的启动速度比父母快,您可能会看到:

    1. 孩子打电话pause
    2. 父母向孩子发送 SIGUSR1。
    3. 家长致电pause
    4. 孩子已解锁并打印Catched
    5. 子打印Child
    6. 子进程向进程组发送 SIGUSR1。
    7. 子打印Catched
    8. 孩子打电话pause
    9. 父级已解锁并打印Catched
    10. 父级退出。

    我认为孩子没有办法退出:它调用pause 两次,虽然它最多可以接收两个信号,但其中一个是从它自己发送的(来自kill(0,SIGUSR1) 的那个)和那一个是同步交付的,而不是在pause的执行期间。

    这个程序可能不是你要写的,但由于你没有描述预期的行为,所以不可能说出你写的是什么意思。我确实注意到您没有遵循分叉程序的通常结构:

    pid = fork();
    if (pid < 0) {
        /*error handling*/
    } else if (pid == 0) {
        /*child code*/
        exit(...); /*Usually, what follows the if is specific to the parent.*/
    }
    

    【讨论】:

    • 那个“kill”(即“send signal”)被意外地调用了两次(一次在父母中,一次在孩子中)正是我一开始想说的。更糟糕的是,孩子(再次,无意中)发送了“kill 0”。那是主要问题。次要问题可能是行缓冲(影响显示的顺序和时间线)。
    • @paulsm4 当输出到终端时,stdout 是行缓冲或非缓冲的;这是由 POSIX 指定的,更一般地在 C 中指定(参见例如 N1256 §7.19.3.7)。
    • 您的报价:“stdout 是行缓冲或非缓冲”。默认情况下,几乎所有 *nix(包括Linux)stdio 默认是“缓冲的”。而 C99 标准与“终端 I/O”到底有什么关系???
    • 当输出为终端时,@14moose Stdio 默认为 line 缓冲。在所有 C 实现上。 C 标准(我引用了 C99,但 C89 有类似的语言)是相关的,因为这是一个 C 程序(POSIX 通过引用包含 C 标准)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-29
    • 1970-01-01
    • 2018-07-13
    • 2019-08-05
    • 1970-01-01
    相关资源
    最近更新 更多