【问题标题】:How can I get the PID of a child process that died, in the parent process in perl?如何在 perl 的父进程中获取死掉的子进程的 PID?
【发布时间】:2012-05-21 23:53:29
【问题描述】:

我正在使用 proc::queue 来管理一堆子进程,然后像这样将它们关闭:

if(Proc::Queue::running_now() < $kids)
  {
    logger("Starting another server process...");
    my $newprocessid = Proc::Queue::run_back(\&serverprocess);
    logger("New child process id: $newprocessid");
    $childprocessids{$newprocessid} = 1;
  }

所以现在我有一个子进程 ID 的哈希值,当父进程发送一个 kill SIGTERM 时我可以杀死它。

但是如果子进程被外部杀死怎么办?父进程知道要启动另一个子进程来弥补丢失的子进程,而新的子进程 PID 最终会出现在我的哈希中,但是父进程如何找出死去的子进程的 PID 以将其从哈希中删除,所以我以后不要尝试杀死它?

我可以设置 $SIG{CHLD},但我不知道如何让子 PID 将其从我的哈希中删除。

【问题讨论】:

    标签: perl fork


    【解决方案1】:

    您可以将父进程和它的子进程放在他们自己的进程组中,并通过向父进程发送信号来杀死整个家庭。

    根据您的问题的性质,您可能愿意 kill 离开(McManus 先生!)并忍受由于每次尝试 kill 对已经死亡的子进程而导致的失败。

    如果父进程除了跟踪孩子什么都不做,那么您只需要一个简单的waitpid 循环即可。

    while ((my $kid = waitpid -1, 0) > 0) {
      warn "$0: [$$] reaped $kid\n";
      delete $kid{$kid};
    }
    

    如果父进程中有其他处理,将第二个参数中的WNOHANG 位设置为waitpid 告诉系统调用不要阻塞。这允许您定期获取僵尸子节点,然后返回到其他处理。

    出于演示目的,假设我们启动了一群昏昏欲睡的孩子。

    #! /usr/bin/env perl
    
    use strict;
    use warnings;
    
    use 5.10.0;  # for defined-or
    
    my %kid;
    
    for (1 .. 5) {
      my $pid = fork // die "$0: fork: $!";  # / fix SO hilighting
      if ($pid == 0) {
        warn "$0: [$$] sleeping...\n";
        sleep 10_000;
        exit 0;
      }
      else {
        $kid{$pid} = 1;
        warn "$0: [$$] forked $pid\n";
      }
    }
    

    然后为了模拟来自外部的杀戮,我们派另一个孩子随机挑选其余的兄弟姐妹。

    my $pid = fork // die "$0: fork: $!";
    if ($pid == 0) {
      warn "$0: [$$] The killer awoke before dawn.\n";
      while (keys %kid) {
        my $pid = (keys %kid)[rand keys %kid];
        warn "$0: [$$] killing $pid...\n";
        kill TERM => $pid or warn "$0: [$$] kill $pid: $!";
        delete $kid{$pid};
        sleep 1;
      }
      exit 0;
    }
    

    现在上面的循环读取讣告。

    while ((my $kid = waitpid -1, 0) > 0) {
      warn "$0: [$$] reaped $kid\n";
      delete $kid{$kid};
    }
    

    仔细检查没有人活着。

    if (keys %kid) {
      my $es = keys %kid == 1 ? "" : "es";
      die "$0: unkilled process$es:\n",
          map "  - $_\n", keys %kid;
    }
    

    输出:

    ./waitpid-demo: [1948] 分叉 7976
    ./waitpid-demo: [7976] 正在睡觉...
    ./waitpid-demo: [1948] 分叉 7244
    ./waitpid-demo: [7244] 正在睡觉...
    ./waitpid-demo: [1948] 分叉 4776
    ./waitpid-demo: [4776] 正在睡觉...
    ./waitpid-demo: [1948] 分叉 4304
    ./waitpid-demo: [4304] 正在睡觉...
    ./waitpid-demo: [1948] 分叉 7908
    ./waitpid-demo: [7908] 正在睡觉...
    ./waitpid-demo: [5144] 杀手在黎明前醒来。
    ./waitpid-demo: [5144] 杀死 7908...
    ./waitpid-demo: [1948] 收获 7908
    ./waitpid-demo: [5144] 杀死 7976...
    ./waitpid-demo: [1948] 收获 7976
    ./waitpid-demo: [5144] 杀死 4776...
    ./waitpid-demo: [1948] 收获 4776
    ./waitpid-demo: [5144] 杀死 4304...
    ./waitpid-demo: [1948] 收获 4304
    ./waitpid-demo: [5144] 杀死 7244...
    ./waitpid-demo: [1948] 收获 7244
    ./waitpid-demo: [1948] 收获 5144

    【讨论】:

    • 不是祖父母,只是父母。并且会有一些外部因素在起作用,有人可能会杀死其中一个子进程。这将是偶然或故意发生的,我只是想确保如果 pid 被重用,我不会意外杀死不属于我的东西。我喜欢也许流程组是要走的路。
    • 这台机器上还运行着其他非常重要的东西(甚至更多),不小心杀死其中一个将是一件大事。
    • 啊,我明白了,waitpid 把 pid 还给你,没想到,谢谢。
    【解决方案2】:

    如果唯一的问题是“从哈希中删除它,这样我以后就不会尝试杀死它”,您可以尝试以下操作:

    # probably you should put this code in a loop for all your process IDs
    $alive = kill 0, $childprocessids{$processId};
    
    if ( $alive ) {
        # process is still alive, do whatever you want with it
    }
    else {
        # process is dead, probably killed externally
        # do whatever you need for this scenario or just delete your hash key
    }
    

    【讨论】:

    • 这里的问题是,如果以前的子进程 ID 被不是我的孩子的其他东西重用,然后我去杀死它(如果我有许可的话)它这将是一个糟糕的场景。
    • 是的,我认为你是对的,可能是罕见的事件,但有机会
    猜你喜欢
    • 2015-01-05
    • 2014-12-11
    • 2014-06-25
    • 2010-09-16
    • 2018-02-23
    • 1970-01-01
    • 1970-01-01
    • 2023-04-04
    • 2021-08-08
    相关资源
    最近更新 更多