如何在perl程序中查找打开的全局文件句柄
我刚刚发现了一个问题,我必须关闭所有打开的文件句柄才能继续我的ApacheCGI脚本。我将问题追溯到Parse::RecDescent如何在perl程序中查找打开的全局文件句柄,perl,filehandle,Perl,Filehandle,我刚刚发现了一个问题,我必须关闭所有打开的文件句柄才能继续我的ApacheCGI脚本。我将问题追溯到Parse::RecDescent #!/usr/bin/env perl use strict; use warnings; use feature qw/say/; $|++; print "Content-Type: text/plain\n\n"; use Parse::RecDescent; say "$$: pre-fork: ". time; if(my $pid = fo
#!/usr/bin/env perl
use strict;
use warnings;
use feature qw/say/;
$|++;
print "Content-Type: text/plain\n\n";
use Parse::RecDescent;
say "$$: pre-fork: ". time;
if(my $pid = fork) {
# parent
say "$$: return immediately: ". time;
}
else {
# child
say "$$: kicked off big process: ". time;
close STDIN;
close STDOUT;
close STDERR;
# close *{'Parse::RecDescent::ERROR'};
sleep 5;
}
我的问题是如何找到所有打开的包文件句柄
我知道fileno
将为打开的文件句柄返回一个计数器。
有没有办法对这些文件进行反向查找,或者通过它们的
fileno
计数器关闭文件句柄?在某些系统上,“/proc/$$/fd/”
返回的目录包含打开的文件描述符列表。您可以使用POSIX::close
来关闭它们
# close all filehandles
for (glob "/proc/$$/fd/*") { POSIX::close($1) if m{/(\d+)$}; }
您可以通过软件包树进行下载:
use strict;
use warnings;
use constant BREAK_DESCENT => {};
use Carp qw<croak>;
use English qw<$EVAL_ERROR>; # $@
sub break_descent {
return BREAK_DESCENT if defined wantarray;
die BREAK_DESCENT;
}
sub _package_descend {
my ( $package_name, $stash, $selector ) = @_;
my $in_main = $package_name =~ m/^(?:main)?::$/;
foreach my $name ( keys %$stash ) {
next if ( $in_main and $name eq 'main::' );
my $full_name = $package_name . $name;
local $_ = do { no strict 'refs'; \*$full_name; };
my $return
= $name =~ m/::$/
? _package_descend( $full_name, *{$_}{HASH}, $selector )
: $selector->( $package_name, $name => $_ )
;
return BREAK_DESCENT if ( ref( $return ) and $return == BREAK_DESCENT );
}
return;
}
sub package_walk {
my ( $package_name, $selector )
= @_ == 1 ? ( '::', shift )
: @_
;
$package_name .= '::' unless substr( $package_name, -2 ) eq '::';
local $EVAL_ERROR;
eval {
no strict 'refs';
_package_descend( $package_name, \%$package_name, $selector );
};
return unless $EVAL_ERROR;
return if do { no warnings 'numeric'; $EVAL_ERROR == BREAK_DESCENT; };
say STDERR $EVAL_ERROR;
croak( 'Failed in selector!' );
}
package_walk( sub {
my ( $pkg, $name ) = @_;
#say "$pkg$name";
# to not close handles in ::main::
#return if $pkg =~ m/^(?:main)?::$/;
# use IO::Handle methods...
map { defined and $_->opened and $_->close } *{$_}{IO};
});
使用严格;
使用警告;
使用常数BREAK_DESCENT=>{};
使用鲤鱼qw;
使用英语qw;#$@
子中断_下降{
如果定义为WANTARY,则返回中断下降;
冲模断裂;
}
分包{
我的($package\u name,$stash,$selector)=@;
my$in_main=$package_name=~m/^(?:main)?:$/;
foreach my$name(键%$stash){
下一个if($in_main和$name eq'main::');
我的$full\u name=$package\u name.$name;
local$\=do{no strict'refs';\*$full\u name;};
我的$return
=$name=~m/::$/
?_package_down($full_name,*{${HASH},$selector)
:$selector->($package\u name,$name=>$)
;
返回中断下降,如果(ref($return)和$return==中断下降);
}
返回;
}
分包步行{
我的($package\u name,$selector)
=@==1?(“:”,shift)
: @_
;
$package_name.='::'除非substr($package_name,-2)eq':';
本地$EVAL_错误;
eval{
没有严格的“参考文献”;
_包名称($package\u name,\%$package\u name,$selector);
};
返回,除非$EVAL_错误;
如果do{no warnings'numeric';$EVAL_ERROR==BREAK_dence;},则返回;
说STDERR$EVAL_错误;
croak('选择器失败!');
}
套餐步行(sub{
我的($pkg,$name)=@;
#说出“$pkg$name”;
#不关闭::main:中的句柄:
#如果$pkg=~m/^(?:main)?:$/,则返回;
#使用IO::Handle方法。。。
映射{defined and$\->opened and$\->close}*{$\}{IO};
});
用一个保存它创建的所有句柄列表的版本全局覆盖open
怎么样?类似这样的事情可能是一个开始:
use Scalar::Util 'weaken';
use Symbol ();
my @handles;
BEGIN {
*CORE::GLOBAL::open = sub (*;$@) {
if (defined $_[0] and not ref $_[0]) {
splice @_, 0, 1, Symbol::qualify_to_ref($_[0])
}
my $ret =
@_ == 1 ? CORE::open $_[0] :
@_ == 2 ? CORE::open $_[0], $_[1] :
CORE::open $_[0], $_[1], @_[2 .. $#_];
if ($ret) {
push @handles, $_[0];
weaken $handles[-1];
}
$ret
}
}
sub close_all_handles {
$_ and eval {close $_} for @handles
}
open FH, $0;
say scalar <FH>; # prints "use Scalar::Util 'weaken';"
close_all_handles;
say scalar <FH>; # error: readline() on closed file handle
使用标量::Util“弱化”;
使用符号();
我的@手柄;
开始{
*核心::全局::开放=子(*;$@){
if(定义为$\u[0]而非参考$\u[0]){
拼接@uu,0,1,符号::将_限定为_参考($u[0])
}
我的$ret=
@_==1?核心::打开$\u0]:
@_==2?核心::打开$\[0],$\[1]:
核心:开放$[0]、$[1]、@[2..$#u3];
如果($ret){
推@handles,$u0];
削弱美元手柄[-1];
}
$ret
}
}
子关闭所有控制柄{
@handles的$\和eval{close$\}
}
开放式FH,0美元;
说标量;#打印“使用标量::Util‘弱化’;”
关闭所有手柄;
说标量;#错误:已关闭文件句柄上的readline()
这将捕获所有全局句柄,甚至是创建但从未清理(由于循环引用或其他原因)的任何词法句柄
如果您在调用
use Parse::recdence
之前放置此覆盖(BEGIN
块),则它将覆盖模块对open
的调用。我最终使用了@ikegami的建议,但我对@Axeman的方法感兴趣。这是一个简化的版本
# Find all file-handles in packages.
my %seen;
sub recurse {
no strict 'refs';
my $package = shift or return;
return if $seen{$package}++;
for my $part (sort keys %{$package}) {
if (my $fileno = fileno($package.$part)) {
print $package.$part." => $fileno\n";
}
}
for my $part (grep /::/, sort keys %{$package}) {
(my $sub_pkg = $package.$part) =~ s/main:://;
recurse($sub_pkg);
}
}
recurse('main::');
为了引起池上的好奇心,在跟踪close-on exec的详细信息时,我想我发现如果您只是在执行另一个进程,您只需关闭
STDIN
、STDOUT
,然后自己关闭STDERR
:
$SYSTEM\u FD\u MAX
$^F最大系统文件描述符,通常为2。
系统文件描述符被传递给exec()ed
进程,而更高的文件描述符则不是。
此外,在open()期间,系统文件描述符
即使open()失败也会保留。(普通档案)
描述符在打开()之前关闭
已尝试。)文件的执行关闭状态
描述符将根据
更新相应的文件、管道或套接字时的$^F
已打开,而不是exec()的时间。
当然,如果您的长期任务不需要运行
execve(2)
调用,那么close-on-exec标志对您毫无帮助。这完全取决于sleep 5是什么的替身。在堆栈、词典等中找不到句柄。他试图关闭所有句柄。我希望能看到一篇关于exec的贴子。我对它了解得不够。@ikegami并不打算详尽无遗,只需回答以下问题:“我的问题是如何找到所有打开的包文件句柄?”封闭的词法作用域不应该是一个问题,因为Perl会为您清理它们,但在包变量中。。。我要补充一点。不,词汇中的句柄在这里是不闭合的。他想在退出之前在孩子身上做一些事情。我喜欢这样简单。@ikegami:关于关闭执行标志:Perl的open()
将使用$^F
的值来确定新打开的文件是否设置了关闭执行标志。$^F
表示stdin、stdout、stderr“截止”值--$^F
上面的文件描述符获取在打开()时设置的close-on-exec位。(不是exec()
time。)由于stdin、stdout和stderr是在脚本执行之前打开的,$^F
不会影响它们是否在exec()期间关闭。(顺便说一句,我读这篇文章的意思是,只关闭STDIN
、STDOUT
和STDERR
是必要的,因为$^F=2。)<