Ruby在哪里追踪其打开的文件描述符?

25

这个问题不是关于

本问题与如何使用File#close或File#open块语法自动关闭文件无关。而是关于Ruby在运行时存储其打开文件描述符列表的位置。

实际问题

如果您有一个具有打开描述符的程序,但没有访问相关的File或IO对象,如何找到对当前打开文件描述符的引用?以此示例为例:

filename='/tmp/foo'
%x( touch "#{filename}" )
File.open(filename)
filehandle = File.open(filename)

第一个文件实例被打开,但对象的引用没有存储在变量中。第二个实例被存储在filehandle中,我可以很容易地使用#inspect或#close访问它。

然而,被丢弃的File对象并没有消失;它只是以任何明显的方式不可访问。在对象被终结之前,Ruby必须在某个地方跟踪它...但是在哪里?

1个回答

41

TL; DR

所有文件和IO对象都存储在ObjectSpace中。

Answer

ObjectSpace类表示:

ObjectSpace模块包含一些与垃圾收集系统交互的例程,并允许您使用迭代器遍历所有活动对象。

如何测试

我在Ruby 1.9.3p194上的控制台上进行了测试。

测试夹具非常简单。想法是有两个具有不同对象标识符的File对象,但只有一个可以通过变量直接访问。另一个在“某个地方”。

# Don't save a reference to the first object.
filename='/tmp/foo'
File.open(filename)
filehandle = File.open(filename)

我随后探索了与文件对象交互的不同方式,即使我没有使用显式对象引用。一旦了解ObjectSpace,这其实是非常容易的。

# List all open File objects.
ObjectSpace.each_object(File) do |f|
  puts "%s: %d" % [f.path, f.fileno] unless f.closed?
end

# List the "dangling" File object which we didn't store in a variable.
ObjectSpace.each_object(File) do |f|
  unless f.closed?  
    printf "%s: %d\n", f.path, f.fileno unless f === filehandle
  end
end

# Close any dangling File objects. Ignore already-closed files, and leave
# the "accessible" object stored in *filehandle* alone.
ObjectSpace.each_object(File) {|f| f.close unless f === filehandle rescue nil}

结论

可能有其他方法来实现这个目标,但这是我用来解决自己问题的答案。如果您知道更好的方法,请发布另一个答案。这将使世界变得更美好。


4
如果你关心那些不仅是磁盘上实际文件的任何开放文件描述符,你可以修改上述代码为:ObjectSpace.each_object(IO) { |f| puts f unless f.closed? }。请注意,不要更改原文意思,并尽可能保持通俗易懂。 - Liron Yahdav
我想知道一个(Ruby)子进程(即在fork之后)是否会看到每个(继承的)打开文件描述符的File对象。 - Bill Burcham
非常有帮助,谢谢。为了详细说明Liron的观点,使用这种方法时,我认为应该考虑包括或排除哪些IO子类(例如,您是否想要标准输入、标准输出、标准错误输出?)。此外,尽管对某些人来说可能很明显,但是这个表达式将返回所有打开的IO:ObjectSpace.each_object(IO).reject(&:closed?)在Ruby 3.0.1的irb中,它为我返回了以下内容:[#<IO:<STDERR>>, #<IO:<STDOUT>>, #<IO:<STDIN>>, #<IO:fd 1>, #<IO:fd 0>]。 - Keith Bennett

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