2012-01-13 10 views
5

私は、Apache cgiスクリプトがオープンするために開いているすべてのファイルハンドルを閉じなければならない問題を追跡しました。私はこの問題をParse :: RecDescentに追跡しました。perlプログラムでオープンなグローバルファイルハンドルを見つける方法

#!/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カウンタでファイルハンドルを閉じる方法はありますか?

答えて

8

一部のシステムでは、"/proc/$$/fd/"によって返されるディレクトリには、開いているファイル記述子のリストが含まれています。 POSIX::closeを使用して閉じることができます。

# close all filehandles 
for (glob "/proc/$$/fd/*") { POSIX::close($1) if m{/(\d+)$}; } 
+0

私はこれの単純さが大好きです。 – CoffeeMonster

+2

@ikegami:close-on-execフラグについて:Perlの 'open()'は、 '$^F'の値を使って、新しくオープンされたファイルがclose-on-execフラグを設定するかどうかを決定します。 '$^F'はstdin、stdout、stderrの" cutoff "値を表します。' $^F 'の上にあるファイル記述子は 'open()' _の時刻にclose-on-execビットを設定します。 stdin、stdout、stderrはスクリプトの実行前に開かれているので、 '$^F'は' exec() '中に閉じても影響を与えません。 (ちなみに、これはデフォルトで '$^F = 2'として' STDIN'、 'STDOUT'、' STDERR'だけを閉じることが必要であることを意味しています) – sarnold

+0

@sarnold、$^Fについて話すのは素晴らしいです。それは私が欠けていたビットです。私はIPC :: Open3のコードを書いてからハンドルについてclose-on-execを設定して以来、私はこれについてもっと知っていると思うでしょう! – ikegami

2

あなたは、パッケージツリーを降りることができます。

use strict; 
use warnings; 
use constant BREAK_DESCENT => {}; 

use Carp qw<croak>; 
use English qw<$EVAL_ERROR>; # [email protected] 

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}; 
}); 
+0

スタック上やレキシカルなどでハンドルが見つからない。すべてのハンドルを閉じようとしている。私はclose-on-execについて言及した記事を見たいと思っていました。私はそれについて十分に知りません。 – ikegami

+0

@ikegami包括的ではありません。次のように答えてください: "私の質問は、どのようにopen * package *ファイルハンドルを見つけるのですか?"クローズドレキシカルスコープは、Perlがそれらをあなたのためにクリーンアップするので問題ではありませんが、パッケージ変数で...私はそれを追加します。 – Axeman

+0

いいえ、字句のハンドルはここで閉じられません。彼は出かける前に子どもの中で物事をしたい。 – ikegami

2

に関するグローバルそれが作成されますハンドルのすべてのリストを保持しているバージョンでopenをオーバーライド何?これは、グローバルハンドルのすべてをキャッチする必要があり

use Scalar::Util 'weaken'; 
use Symbol(); 
my @handles; 
BEGIN { 
    *CORE::GLOBAL::open = sub (*;[email protected]) { 
     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 

、および作成しましたが、(原因循環参照またはその他の理由で)クリーンアップされなかったとしても任意の字句ハンドル:このような何かを開始可能性があります。

use Parse::RecDescentのコールの前にこのオーバーライド(BEGINブロック)を配置すると、モジュールが作成するopenへのコールが上書きされます。

+0

はいファイルハンドルハンドラもうまくいくはずです:) – CoffeeMonster

1

私は@池上の提案を使い終わったが、@ Axemanの方法に興味があった。ここに簡略版があります。池上の好奇心のためのクローズ(close-on-exec)の詳細を追跡するときは、単に別の処理を実行している場合

# 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::'); 
3

、私は自分自身を、私はあなたがする必要があるすべては、STDOUT近いSTDINであることがわかったと思うし、STDERR

$SYSTEM_FD_MAX 
    $^F  The maximum system file descriptor, ordinarily 2. 
      System file descriptors are passed to exec()ed 
      processes, while higher file descriptors are not. 
      Also, during an open(), system file descriptors are 
      preserved even if the open() fails. (Ordinary file 
      descriptors are closed before the open() is 
      attempted.) The close-on-exec status of a file 
      descriptor will be decided according to the value of 
      $^F when the corresponding file, pipe, or socket was 
      opened, not the time of the exec(). 

もちろん、長寿命のタスクでexecve(2)コールを実行する必要がない場合は、close-on-execフラグはまったく役に立ちません。それはすべてsleep 5が何のためのものであるかに依存します。

関連する問題