如何在perl程序中查找打开的全局文件句柄

如何在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

我刚刚发现了一个问题,我必须关闭所有打开的文件句柄才能继续我的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 = 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。)<