首页 > 解决方案 > Perl:fork(),避免僵尸进程和“无子进程”错误

问题描述

我有一个 Perl 应用程序,它在 RH 系统上运行了几年,基本上没有问题。在一个地方,我必须运行一个可能需要几分钟才能完成的系统命令,所以我在一个子进程中执行此操作。整体结构是这样的:

    $SIG{CHLD} = 'IGNORE'; # Ignore dead children, to avoid zombie processes
    my $child = fork();

    if ($child) {    # Parent; return OK
       $self->status_ok();  
    } else {         # Child; run system command
         # do a bunch of data retrieval, etc.
         my $output;
         my @command = # generate system command here
         use IPC::System::Simple 'capture';
         eval { $output = capture(@command); };
         $self->log->error("Error running @command: $@") if $@;
         # success: log $output, carry on
    }

我们最近改变了我们的一些基础设施,虽然没有以我预期的方式对此产生任何影响。(仍在 RH 上运行,仍在使用 nginx 等)但是,现在我们发现运行此代码的几乎每个实例都失败了,记录 'Error running {command}: failed to start: "No child processes" at /path/to /code.pl'。

我环顾四周,无法弄清楚正确的解决方案是什么。有人建议$SIG{CHLD}从“忽略”更改为“默认”,但我不得不担心僵尸进程。

是什么导致“无子进程”错误,我们如何解决这个问题?

标签: perlipc

解决方案


有人建议将 $SIG{CHLD} 从“IGNORE”更改为“DEFAULT”,但我不得不担心僵尸进程。

这不是真的。

僵尸进程是一个已经结束但尚未被其父进程收割的进程。父母使用wait(2)、waitpid(2) 或类似的方法来收获其孩子。capture等待它的孩子结束,所以它不会留下任何僵尸。

实际上,您得到的错误来自waitpid. capture正在等待孩子结束收割并收集其错误代码,但是您指示操作系统在完成后立即清理孩子,waitpid没有孩子可以收割,也没有错误代码可以收集。

要解决此问题,只需local $SIG{CHLD} = 'DEFAULT';在调用capture.


推荐阅读