我经常在也有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 } }
?
答案 0 :(得分:18)
我认为没有办法阻止END块在分叉进程中运行,但这应该让你检测它:
my $original_pid; BEGIN { $original_pid = $$ }
... # Program goes here
END { do_cleanup() if $$ == $original_pid }
答案 1 :(得分:15)
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块。通常有更好的方法。