不关闭Perl中的目录句柄会有什么影响?

7

我最近继承了别人编写的一些代码。

我发现在代码中每次打开目录进行读取时,由于原始开发者存在语法问题 - 他使用了 close 函数来尝试关闭目录句柄,而不是 closedir 函数,因此从未关闭目录。

代码大致如下:

opendir( DIR, $dir ) or die "Cannot open $dir: $!\n";
@files = readdir( DIR );
close( DIR );

(这也是《Perl最佳实践》(第208、278页)中关于检查close函数返回值的另一个好处。如果在此情况下检查close的返回值,它将失败并显示“坏文件号”)。)

我已经改为使用closedir,但这让我开始思考:既然从未关闭目录句柄,保持目录句柄打开时间很长会有什么负面影响呢?

此程序较大(3,500行代码),运行一段时间(5-10分钟),同时运行多个该程序的实例。在上述示例中,如果有10个该程序的实例同时运行,则所有实例都会针对同一目录保持打开的目录句柄5分钟或更长时间。我相信当程序完成时,Perl会自动关闭目录句柄,但最佳实践是尽快关闭它。

对我来说,留下文件句柄不关闭可能会导致问题更加明显(特别是对于打开写入文件句柄的情况),但是不关闭目录句柄会发生什么不良后果呢?

我提出这个问题的原因是因为有一个奇怪的情况,即该程序试图创建一个文件(在$dir定义的目录中)。文件名中嵌入了PID,因此文件已经存在的可能性较小,但是Perl无法将该文件打开进行写操作,因为它说该文件已经存在。当我们查看目录时,该文件并不存在。我想知道这个目录上的所有打开目录句柄是否会导致这样的问题?

我不确定操作系统是否有影响,但该程序正在AIX上运行。

提前感谢您的帮助,并祝愉快的星期五!

3个回答

12

你浪费了一个目录描述符 - 这可能算作一个文件描述符。如果你的程序打开了足够多的目录以用完文件描述符,那么最终会对你造成伤害。否则,它是相当无害的,尽管不是理想的。这使得系统(以及Perl)保留了它本来可以释放的资源。

如果目录句柄是一个本地变量而不是一个普通的DIR-style名称,你可能会让Perl替你清理干净。请参见opendir,它说:

打开一个名为EXPR的目录,供readdir、telldir、seekdir、rewinddir和closedir使用。成功时返回true。DIRHANDLE可以是一个表达式,其值可以用作间接目录句柄,通常是真实的目录句柄名称。如果DIRHANDLE是未定义的标量变量(或数组或哈希元素),该变量将被赋予一个新匿名目录句柄的引用。DIRHANDLE具有与FILEHANDLE分开的自己的命名空间。


1
即使在今天,一些操作系统对文件描述符仍然非常吝啬(我在看你,Solaris,以及你默认的64-256设置)。 - mob
8
@mobrule - 对不起,由于没有可用的文件描述符,我无法发布您的评论 - 真诚地,StackOverflow Solaris 后端。 - DVK

9

不会有太大的影响。内核本身的内存使用量会略微增加,它无法释放用于循环遍历目录条目列表的迭代器,可能也会从perl方面增加。

此外,只要打开某个目录的任何描述符,数据就无法从文件系统中实际删除。如果其他外部进程删除了您拥有句柄的目录,则该目录将停止出现在未来的目录列表中,但数据仍必须保留在磁盘上,并且仍可被具有已打开句柄的进程访问。例如,这可能导致磁盘使用量出现奇怪的数字。

还要注意,您不一定需要手动关闭所有句柄。当使用词法文件句柄时,只要对句柄的最后一个引用消失,关闭就会自动发生:

 { # new scope
     opendir(my $handle, ...) or ...;
     ...
 } # implicit closedir happens here

7

这是一篇关于始终使用词法文件(和目录)句柄的教训 - 词法句柄在超出作用域时会自动关闭。

因此,只有在以下情况下才会浪费描述符(如Jonathan所述):1.您使用了旧式的glob句柄;2.所有代码都在一个平面脚本中,没有子例程或其他作用域。使用良好的编程实践,无意中的错误将更少 :)


这个 opendir 函数在主函数中,因此它是全局作用域的。你说得没错 - 良好的编程实践肯定会减少无意中的错误! - BrianH
1
根据你的代码,你仍然使用了裸字柄。除非你明确地进行本地化,否则它们始终是全局作用域的。 - rafl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接