【问题标题】:Perl: fork(), avoiding zombie processes, and "No child processes" errorPerl:fork(),避免僵尸进程和“无子进程”错误
【发布时间】:2019-12-31 22:36:51
【问题描述】:

我有一个 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} 从“IGNORE”更改为“DEFAULT”,但我不得不担心僵尸进程。

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

【问题讨论】:

  • 当父进程没有在子进程的退出代码上wait 时,你会得到僵尸进程。 status_ok() 是做什么的?这会退出程序吗?等孩子?您可能应该处理 SIG_CHLD; stackoverflow.com/questions/1608017/… 可能会有所帮助。
  • 这是在一个web框架中; status_ok() 基本上是向前端发送一条消息,说“您的请求正在处理中;您应该很快收到包含完整详细信息的电子邮件”,然后退出(我们在孩子的系统命令完成时发送电子邮件)。我们不会在任何地方处理SIG_CHLD,我也不确定如何处理。但同样,这已经运行了多年,直到最近才出现问题。
  • 评论应改为“忽略死去的孩子,确保他们变成僵尸”
  • 如果父级通常在发送响应后退出,则子级将被 pid 1 继承并立即等待。如果你现在得到僵尸,那是因为父母没有退出。尝试发送响应时可能会阻塞。
  • 我会添加一些监控代码。在父级中,使用kill 0, $childps 监视子级。在孩子中,在执行之前检查命令,例如ls -la $command[0]。解码 $?和美元!。尝试手动生成,而不是使用 IPC::System::Simple。尝试 IPC::Cmd。尝试手动运行 shell 提示符下包含的任何 @command。尝试在命令前添加strace。无法启动的命令可能有奇怪的原因。也许你需要变基。也许一个新的库正在挂钩您的流程并破坏它。 HTH。

标签: perl ipc


【解决方案1】:

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

这不是真的。

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

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

要解决此问题,只需将local $SIG{CHLD} = 'DEFAULT'; 放在对capture 的调用之前。

【讨论】:

  • 另外,检查 fork 是否失败而不是假设 false 意味着您在孩子中是个好主意
  • @ysth,这不应该是对 OP 的评论吗?无论如何,代码显然缺少部分(例如,孩子中缺少 exit),但我决定简单地回答这个问题,而不是推测 OP 的实际代码。同样,OP 的代码显然需要代码审查(例如,运行时代码中间的 use 语句),但我不想偏离主题。
  • 是的,为了简单起见,我确实修改了代码,如果造成混乱,我深表歉意。原件确实有CORE::exit(0);在孩子,它实际上并没有在代码中间有use IPC::System::Simple,但我想弄清楚capture来自哪里,而不用费心表明它是300以上行等。无论如何,这个解决方案对我有用,所以,非常感谢!
  • @ikegami 够公平
猜你喜欢
  • 1970-01-01
  • 2010-11-12
  • 2017-08-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-15
  • 1970-01-01
相关资源
最近更新 更多