【问题标题】:Is there a limit to the number of file handles I can open in Perl?我可以在 Perl 中打开的文件句柄的数量是否有限制?
【发布时间】:2011-10-21 04:09:45
【问题描述】:

我正在设置一个包含文件句柄的哈希引用。

我的输入文件的第四列包含一个标识符字段,我用它来命名文件句柄的目标:

col1    col2    col3    id-0008    col5
col1    col2    col3    id-0002    col5
col1    col2    col3    id-0001    col5
col1    col2    col3    id-0001    col5
col1    col2    col3    id-0007    col5
...
col1    col2    col3    id-0003    col5

我使用 GNU 核心实用程序来获取标识符列表:

$ cut -f4 myFile | sort | uniq
id-0001
id-0002
...

此列中可以有超过 1024 个唯一标识符,我需要为每个标识符打开一个文件句柄并将该句柄放入哈希引用中。

my $fhsRef;
my $fileOfInterest = "/foo/bar/fileOfInterest.txt";

openFileHandles($fileOfInterest);
closeFileHandles();

sub openFileHandles {                                                                                                                                                                                                              
    my ($fn) = @_;                                                                                                                                                                                                              

    print STDERR "getting set names... (this may take a few moments)\n";                                                                                                                                                           
    my $resultStr = `cut -f4 $fn | sort | uniq`;                                                                                                                                                                 
    chomp($resultStr);                                                                                                                                                                                                             
    my @setNames = split("\n", $resultStr);                                                                                                                                                                                        

    foreach my $setName (@setNames) {                                                                                                                                                                                              
        my $destDir = "$rootDir/$subDir/$setName"; if (! -d $destDir) { mkpath $destDir; }                                                                                                                                          
        my $destFn = "$destDir/coordinates.bed";                                                                                                                                                                                   
        local *FILE;                                                                                                                                                                                                               
        print STDERR "opening handle to: $destFn\n";                                                                                                                                                                               
        open (FILE, "> $destFn") or die "could not open handle to $destFn\n$!\n";                                                                                                                                                  
        $fhsRef->{$setName}->{fh} = *FILE;                                                                                                                                                                                         
        $fhsRef->{$setName}->{fn} = $destFn;                                                                                                                                                                                       
    }                                                                                                                                                                                                                              
}                                                                                                                                                                                                                                  

sub closeFileHandles {                                                                                                                                                                                                             
    foreach my $setName (keys %{$fhsRef}) {                                                                                                                                                                                        
        print STDERR "closing handle to: ".$fhsRef->{$setName}->{fn}."\n";                                                                                                                                                         
        close $fhsRef->{$setName}->{fh};                                                                                                                                                                                           
    }                                                                                                                                                                                                                              
}       

问题是我的代码相当于id-1022

opening handle to: /foo/bar/baz/id-0001/coordinates.bed
opening handle to: /foo/bar/baz/id-0002/coordinates.bed
...
opening handle to: /foo/bar/baz/id-1022/coordinates.bed
could not open handle to /foo/bar/baz/id-1022/coordinates.bed
0
6144 at ./process.pl line 66.

Perl 中我可以打开或存储在哈希引用中的文件句柄数量是否有上限?还是我在其他地方又犯了一个错误?

【问题讨论】:

  • Perl 没有限制,但你的操作系统肯定有。 (似乎是 1024。STDIN+STDOUT+STDERR+1021。)限制可能是可配置的。顺便说一句,你应该打印$!,而不是$?
  • perldoc.perl.org/FileCache.html FileCache 是一个标准模块,应该允许您超过操作系统对打开文件的限制。

标签: perl file-io io


【解决方案1】:

在所有编程语言中,每个进程的打开文件数都有限制。

这实际上是操作系统为防止恶意(或伪造)程序消耗系统的所有资源而施加的限制,这可能导致操作系统冻结。

如果您使用的是基于 Linux(非 Mac)的操作系统,请查看 ulimit/etc/security/limits.conf

ulimit -n 2048

这应该适用于大多数 Linux 发行版。

我不知道 Mac(在这一点上与 Unix 不同)和/或 Windows 的配置。


编辑:

限制 os OS X 是使用launchctl 工具定义的:

launchctl limit maxfiles 2048 unlimited

【讨论】:

  • 在做这种诡计之前,请重新考虑您的方法,看看您是否真的需要同时打开数千个文件。
  • 打开和关闭文件句柄需要时间。如果我可以同时打开所有句柄,那么我就不需要编写和调试代码来管理我不断打开和关闭以保持在限制之下的较小的句柄池。
【解决方案2】:

存在操作系统强加的限制。请注意,stdin/stdout/stderr 都算作 FD。 Linux 上的默认 FD 限制是每个进程 1024。 This question 提供了更多细节。

请注意,我使用的大多数 Linux 的硬限制是 1024。检查/etc/security/limits.conf(路径可能取决于您的发行版),看看您是否可以增加它。

您也可以考虑重写脚本,使其不需要同时打开所有这些文件。要么加载所有数据,要么提供延迟加载机制,以便您在需要时加载数据,然后关闭文件。

【讨论】:

  • 我正在处理的文件大小超出了我的内存容量。这个想法是一次一行地通过文件流式传输并将其拆分为一大组小得多的文件。如果我保持一个句柄池打开,我可以在处理较大文件的每一行时快速写入每个句柄。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-10-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多