如何在子进程中禁用END块?

时间:2010-11-29 19:55:44

标签: perl fork

我经常在也有fork块的程序中使用END { ... }

...
END { &some_cleanup_code }
...
my $pid = fork();
if (defined($pid) && $pid==0) {
    &run_child_code;
    exit 0;
}

子进程在退出时执行END {}块,但通常我不希望这种情况发生。有没有办法阻止子进程在退出时调用END块?除此之外,有没有办法让程序“知道”它是一个子进程,所以我可以这样说

END { unless (i_am_a_child_process()) { &some_cleanup_code } }

8 个答案:

答案 0 :(得分:18)

我认为没有办法阻止END块在分叉进程中运行,但这应该让你检测它:

my $original_pid; BEGIN { $original_pid = $$ }

... # Program goes here

END { do_cleanup() if $$ == $original_pid }

答案 1 :(得分:15)

perldoc -f exit

  

exit()函数并不总是立即退出。它首先调用任何已定义的END例程,但这些END例程本身可能不会中止退出。同样,在真正退出之前调用任何需要调用的对象析构函数。如果这是一个问题,您可以调用POSIX:_exit($status)以避免END和析构函数处理。有关详细信息,请参阅perlmod

答案 2 :(得分:7)

use B;
@{; eval { B::end_av->object_2svref } || [] } = ();

我以为有一个Devel :: module也允许你这样做,但我现在找不到它。

当然,如果您使用可能正在使用END块的任意模块用于自己的目的,则无法安全地执行此操作...

<小时/>

(由OP编辑)您可以使用B::end_av控制END块。作为概念验证:

END { print "This is the first end block.\n"; }
my $END_block_2_line = __LINE__ + 1;
END { print "This is the second end block.\n"; }
END { print "This is the third end block.\n" }

sub disable_specific_END_block {
  use B;
  my ($file, $line) = @_;
  eval {
    my @ENDs = B::end_av->ARRAY;
    for (my $i=$#ENDs; $i>=0; $i--) {
      my $cv = $ENDs[$i];
      if ($cv->START->file eq $file && $cv->START->line == $line) {
        splice @{B::end_av->object_2svref}, $i, 1;
      }
    }
  };
}

disable_specific_END_block(__FILE__, $END_block_2_line);


$ perl endblocks.pl
This is the third end block.
This is the first end block.

通常这样的事情对我所需要的东西来说太过分了,但是我可以看到一些可以派上用场的案例。

答案 3 :(得分:6)

从某种角度来说,在fork ed孩子中避免这些习惯的习惯方式是:

exec "true";

或者,如果您觉得太不值得信任,这些都可以使用,甚至可以使用use strict

exec    $^X =>  -eexit;

exec ~~echo => ~~-echo;

exec ~~echo => !!-echo;

和onomatopoetic

exec reverse reverse echo => ~~-echo;

Rebmemer - 在Perl中,没有愚蠢的问题,但可能会有愚蠢的答案......

$ perl -Mstrict '-leprint ucfirst lc reverse-Remember'

答案 4 :(得分:4)

这是POE :: Wheel :: Run用来做你想要的代码。当$^O eq 'MSWin32'时,POE :: Kernel :: RUNNING_IN_HELL为true。根据您的需求进行调整。

sub _exit_child_any_way_we_can {
  my $class = shift;
  my $exitval = shift || 0;

  # First make sure stdio are flushed.
  close STDIN  if defined fileno(STDIN); # Voodoo?
  close STDOUT if defined fileno(STDOUT);
  close STDERR if defined fileno(STDERR);

  # On Windows, subprocesses run in separate threads.  All the "fancy"
  # methods act on entire processes, so they also exit the parent.

  unless (POE::Kernel::RUNNING_IN_HELL) {
    # Try to avoid triggering END blocks and object destructors.
    eval { POSIX::_exit( $exitval ); };

    # TODO those methods will not exit with $exitval... what to do?
    eval { CORE::kill KILL => $$; };
    eval { exec("$^X -e 0"); };
  } else {
    eval { CORE::kill( KILL => $$ ); };

    # TODO Interestingly enough, the KILL is not enough to terminate this process...
    # However, it *is* enough to stop execution of END blocks/etc
    # So we will end up falling through to the exit( $exitval ) below
  }

  # Do what we must.
  exit( $exitval ); 
}

答案 5 :(得分:2)

运行run_child_code()时,只需设置某种全局可访问标记。

有很多方法可以做到这一点,但我可能会把它放到一个包中,并使用一个包作用域的词法来处理存储。您也可以使用普通变量标志。这有点简单。我喜欢这个包解决方案,因为它可以轻松地在多个模块中工作。

END { &some_cleanup_code unless ImaKid->get; }
my $pid = $fork;
if( defined($pid) && $pid == 0 ) {
    ImaKid->set;
    &run_child_code;
}

BEGIN {
    package ImaKid;

    my $child_flag;

    sub set {
       $child_flag = 1;
    }

    sub get {
       return $child_flag;
    }
}

考虑将fork管理代码包装到同一个包中,为所有与fork相关的需求创建一个一致的API。

答案 6 :(得分:1)

这就是我所做的。 在程序的开头(我知道还没有fork发生) 存储PID。     我的$ parent_pid = $$;

#Then in the end block
END {
    my $current_pid = $$;
    if ( $parent_pid == $current_id ) 
        ##Here goes my cleanup code.
    }
}

答案 7 :(得分:0)

我知道这对你没有多大帮助,但无论如何我会说:不要使用END块。通常有更好的方法。