2012-01-13 2 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 '위의 파일 디스크립터는'close()'_ 시간에 close-on-exec 비트를 설정합니다. ('exec()'시간이 아닙니다.) stdin, stdout, stderr는 스크립트 실행 전에 _ 열리 며'$^F'는'exec()'중에 닫히면 영향을 미치지 않습니다. (덧붙여서, 나는'STDIN','STDOUT' 및'STDERR' 만 기본값으로'$^F = 2'로 필요하다는 것을 알기 위해 이것을 읽었습니다.) – sarnold

+0

@sarnold, 약 $^F에 관해서는 굉장합니다. 그게 내가 놓친 비트 야. 내가 핸들에 close-on-exec을 설정하는 코드를 IPC :: Open3에 작성한 이후로 나는 이것에 대해 더 많이 알고 있다고 생각할 것이다. – 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를 오버라이드 (override)에 대해? 이 글로벌 핸들을 모두 잡을해야

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

@ 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::'); 
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이 어떤 역할을하는지에 달려 있습니다.