【问题标题】:Status of daemonized MCE process affects parent process守护进程的状态影响父进程
【发布时间】:2015-09-01 16:58:21
【问题描述】:

我正在使用 MCE 做一些事情,它运行良好。我需要观察一个事件的发生,然后派生一个 MCE 进程来处理该事件。这很好用,但是当我认为只有子 MCE 进程应该受到影响时,我遇到了一个问题,即子进程中的错误会杀死父进程。这是一个演示此行为的简短程序。

#!/usr/bin/perl

use strict;
use warnings;

use MCE::Loop;
use MCE::Signal '-setpgrp';
use POSIX "setsid";

$SIG{CHLD} = 'IGNORE';

my $mce_maxWorkers = 2;
my $mce_chunkSize = 1;
my @pids;
my $i = 0;

my $name = shift;

while ($i < 2) {
    my $pid = fork();

    if (!defined $pid) {
        print "Can't fork: $!\n";
    }

    elsif ($pid == 0) {

        #setpgrp(0,0);
        (setsid() != -1) || die "Can't start a new session: $!";

        MCE::Loop::init {
            max_workers => $mce_maxWorkers,
            chunk_size => $mce_chunkSize,
            on_post_exit => sub {
                my ($mce, $e) = @_;
                print "$e->{wid}: $e->{pid}: status $e->{status}: $e->{msg}\n";
            }
        };

        my $tail = 'tail -q -f '.$name;
        open my $tail_fh, "-|", $tail or die "Can't open tail\n";

        mce_loop_f {
            my ($mce, $chunk_ref, $chunk_id) = @_;
            my $line = ${$chunk_ref}[0];
            chomp($line);
            print $line."\n";

        } $tail_fh;
        close $tail_fh;

        MCE->shutdown;
        exit;
    }

    else {
        print $pid."\n";
        $i++;
        push(@pids,$pid);
    }
}

foreach my $p (@pids) {
    waitpid $p, 0;
}

当运行这个程序时,分叉两个子进程,它们尾随一个文件并使用一个具有两个工作进程的 MCE 循环读取其内容。这会产生 7 个进程、1 个父进程、2 个 MCE 管理器和 4 个 MCE 工作者(以及 2 个尾进程)。

使用 setsid,MCE 管理器进程应该与父进程分离。任何导致这些孩子死亡的事情都不应该影响父进程对吗?

这是 ps -efj | 的结果grep 监视器

user1  29001   978 29001   978  0 11:41 pts/2    00:00:00 /usr/bin/perl ./monitor1_test.pl tmp/monitor1/test1.log
user1  29002 29001 29002 29002  0 11:41 ?        00:00:00 /usr/bin/perl ./monitor1_test.pl tmp/monitor1/test1.log
user1  29003 29001 29003 29003  0 11:41 ?        00:00:00 /usr/bin/perl ./monitor1_test.pl tmp/monitor1/test1.log
user1  29004 29002 29002 29002  0 11:41 ?        00:00:00 tail -q -f tmp/monitor1/test1.log
user1  29005 29003 29003 29003  0 11:41 ?        00:00:00 tail -q -f tmp/monitor1/test1.log
user1  29006 29002 29002 29002  0 11:41 ?        00:00:00 /usr/bin/perl ./monitor1_test.pl tmp/monitor1/test1.log
user1  29007 29002 29002 29002  0 11:41 ?        00:00:00 /usr/bin/perl ./monitor1_test.pl tmp/monitor1/test1.log
user1  29008 29003 29003 29003  0 11:41 ?        00:00:00 /usr/bin/perl ./monitor1_test.pl tmp/monitor1/test1.log
user1  29009 29003 29003 29003  0 11:41 ?        00:00:00 /usr/bin/perl ./monitor1_test.pl tmp/monitor1/test1.log

如果我要向上面的 29002 发送 SIGTERM,我希望该进程与 29004、29006 和 29007 一起终止。我还希望进程 29001 和 29003 不受影响。

但是,我看到的是 29001 与 29002 一起死亡,而 29003 仍然存在。在终端上观察到以下错误。

shell $ ./monitor1_test.pl tmp/monitor1/test1.log 
29002
29003
test1234
test1234

## monitor1_test.pl: caught signal (INT), exiting

Killed
shell $ MCE::shutdown: method cannot be called while running at /usr/share/perl5/site_perl/MCE/Signal.pm line 371.
END failed--call queue aborted at ./monitor1_test.pl line 371, <$tail_fh> line 1.

为什么其中一个子进程的终止会以这种方式影响父进程?我是否做错了什么或做出了错误的假设,即父母应该度过这个难关?我现在有点烦恼,所以非常感谢任何建议。

平台:Linux 4.0.6 x86_64
Perl:5.22

【问题讨论】:

  • 你发送了 SIGTERM,但父级收到了 SIGINT?
  • kill('INT', $_is_MSWin32 ? -$$ : -getpgrp); in MCE::Signal 可能负责。
  • @ikegami 谢谢你是对的。我在 Signal.pm 上进行了攻击,在许多情况下进程组被终止,并且在这些终止中也引用了 $main_proc_id (父 pid)。我确信有一个适当的方法可以解决它,但这看起来是根本原因。谢谢!
  • 不会打电话给setpgrp 吗?
  • 我很早就尝试过(评论仍在上面的脚本中),但问题仍然存在。该问题似乎与 MCE::Signal 将 HUP、INT、QUIT 和 TERM 分配给 \&stop_and_exit 的事实有关。为了获得我正在寻找的功能,我必须在子进程中使用 setpgrp 在我的脚本中将 $SIG{TERM} 设置回 DEFAULT。这使我可以在不影响父进程组的情况下终止子进程组。

标签: linux perl parallel-processing


【解决方案1】:

模块在模块加载时缓存 PID。通过执行以下 post-fork 来修复它:

$MCE::Signal::main_proc_id = $$;

更好的是,将 MCE 的加载延迟到分叉之后。我会通过移动来做到这一点

use MCE::Loop;
use MCE::Signal '-setpgrp';

进入一个模块(比如Worker.pm),并将子代码移动到同一模块中名为run的子代码中,然后执行以下后分叉:

require Worker;
Worker::run();

script:

#!/usr/bin/perl

use strict;
use warnings;

use POSIX qw( setsid );

my $name = shift;

my @pids;
while (@pids < 2) {
    my $pid = fork();

    if (!defined $pid) {
        print "Can't fork: $!\n";
    }

    elsif ($pid == 0) {
        (setsid() != -1)
            or die "Can't start a new session: $!";

        require Worker;
        Worker::run($name);
        exit;
    }

    else {
        print $pid."\n";
        push(@pids, $pid);
    }
}

for my $pid (@pids) {
    waitpid($pid, 0);
}

Worker.pm:

package Worker;

use strict;
use warnings;

use MCE::Loop;
use MCE::Signal '-setpgrp';

my $mce_maxWorkers = 2;
my $mce_chunkSize  = 1;

sub run {
    my $name = shift;

    MCE::Loop::init {
        max_workers => $mce_maxWorkers,
        chunk_size => $mce_chunkSize,
        on_post_exit => sub {
            my ($mce, $e) = @_;
            print "$e->{wid}: $e->{pid}: status $e->{status}: $e->{msg}\n";
        }
    };

    my $tail = 'tail -q -f '.$name;
    open my $tail_fh, "-|", $tail or die "Can't open tail\n";

    mce_loop_f {
        my ($mce, $chunk_ref, $chunk_id) = @_;
        my $line = ${$chunk_ref}[0];
        chomp($line);
        print $line."\n";

    } $tail_fh;
    close $tail_fh;

    MCE->shutdown;
}

1;

【讨论】:

  • 我必须添加包 Worker;到 Worker.pm 的顶部(我将其重命名),但除此之外,这非常有效!感谢您花时间与我一起完成它。我将能够很快将其应用到我的主程序中。 :)
  • 啊,这周我第二次忘记了。固定。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多