如何跟踪子进程

时间:2014-08-07 22:00:45

标签: linux perl fork

因此,我的程序会生成许多子进程以响应某些事件,并且我正在做一些事情,以便在程序退出时跟踪并终止它们(Perl语法):

my %children = ();

# this will be called upon exit
sub kill_children {
  kill 'INT' => keys %children;
  exit;
}

# main code

while(1) {

   ...
   my $child = fork();

   if ($child > 0) {
     $children{$child} = 1;
   } elsif ($child == 0) {
     # do child work ...
     exit();
   } else {
     # handle the error
   }
}

所以这个想法如上所述。然而,那里存在明显的竞争条件,因为给定的孩子可以在父亲有机会运行并在%子哈希中记录其pid之前开始和终止。因此,即使这个孩子已经终止,父亲也可能最终认为给定的pid属于活跃的孩子。

有没有办法做我想以安全的方式完成的事情?

编辑:为了更好地跟踪孩子,代码可以扩展如下(但是也会受到完全相同的竞争条件的影响,这就是为什么我没有在第一次完全写入它地方):

my %children = ();

sub reap {
  my $child;
  while (($child = waitpid(-1, WNOHANG)) > 0) {
    #print "collecting dead child $child\n";
    delete $children{$child};
  }
}

$SIG{CHLD} = \&reap;

# this will be called upon exit
sub kill_children {
  local $SIG{CHLD} = 'IGNORE';
  kill 'INT' => keys %children;
  exit;
}

# main code

while(1) {

   ...
   my $child = fork();

   if ($child > 0) {
     $children{$child} = 1;
   } elsif ($child == 0) {
     # do child work ...
     exit();
   } else {
     # handle the error
   }
}

即使在这种情况下,%child的内容也可能无法反映实际活跃的孩子。

编辑2:我找到了this question,这正是同样的问题。我喜欢那里建议的解决方案。

1 个答案:

答案 0 :(得分:2)

在UNIX上,它不是竞争条件。这是处理fork()的标准方法。当子进程退出时,其状态将更改为"已终止&#34 ;;它变成zombie。它仍然在进程表中有一个条目,直到父进程调用wait函数之一。只有在那之后才真正消除了死亡过程。

即使父母将自己设置为忽略SIGCHLD,它仍然不符合竞争条件;父母将只有一个无效的PID。在这种情况下,wait()将返回ECHILD。 设置SIGCHLD会释放孩子的PID,可能导致父母试图杀死不是孩子的进程。

在没有fork调用的Windows上,通过在perl进程中创建线程来模拟它。见perlfork。关于Windows是否可能导致竞争状况,我对Windows不足以了解情况,但我怀疑不是。