2013-05-10 61 views
1

我最近在我的服务器上发现了一个关于“打开文件过多”的异常。我检查了lsof,果然,有一堆保持打开的PDF文件(全部在同一个目录中)。这个特定的文件通过Django FileField进行管理。我试图追踪我的项目中任何按名称明确打开文件的地方,并且只有一个地方可以找到,并且我可以告诉文件在那里正确关闭。可能还有其他地方的文件仍处于打开状态,但我不知道如何找出哪些代码实际上保持打开文件。我试着简单地打电话给打开()和文件(),但没有运气。如何找出哪一行代码正在打开文件?

有没有什么办法系统地跟踪哪些代码是负责保持文件打开?

编辑:我明白如何正确打开/关闭文件。我的问题是,是否有一种方法可以跟踪现有的一行代码,而这些代码保留了文件的打开状态。

+0

这可能是一个例外,因为'file.close()'调用永远不会发生。您可以使用'try' /'finally'来避免这种情况,但[with'语句](http://www.youtube.com/watch?v=lRaKmobSXF4)可以帮您。 – 2013-05-10 17:44:22

回答

4

当您使用open时,请尝试将其用作上下文管理器。这样一来,无论发生什么事情,它就会当你用它做封闭:

with open('file.txt', 'r') as fin: 
    # Access fin like normal 

# No matter what happens, after the block, it's closed! 

或者,你可以用自己的功能,为你做一些额外的记录替换的openclose实例:

def my_open(filename, *args): 
    logger.debug('Opening %s' % filename) 
    return open(filename, *args) 

def my_close(file_obj): 
    logger.debug('Closing %s' % file_obj.name) 
    return file_obj.close() 

作为最后的手段,如果您无法访问有问题的代码,或者修改它会很繁琐,那么您可以尝试使用猴子修补功能。

import traceback 
class MyFile(file): 
    @staticmethod 
    def open(*args, **kwargs): 
     return MyFile(*args, **kwargs) 

    def __init__(self, *args, **kwargs): 
     self._file = self._open(*args, **kwargs) 
     print('Opening %s from %s' % (
      self._file.name, ''.join(traceback.format_stack()))) 

    def close(self): 
     print('Closing file %s from %s' % (
      self._file.name, ''.join(traceback.format_stack()))) 
     self._file.close() 

# Now the monkey-patching 
file = MyFile 
MyFile._open = open 
open = MyFile.open 

o = open('hello', 'w+') 

这当然不是在世界上最漂亮的事情,但如果你能猴子补丁,那么你会至少能够处理遗留代码。

+0

我明白在编写新代码时应如何正确地打开/关闭文件,但我的问题是如何找到没有正确关闭文件的现有代码行。 – 2013-05-10 18:17:43

+1

在我最近的编辑中,我建议可以使用“打开”和“文件”来修补猴子。也许不是最有吸引力的想法,但它可以帮助你追踪它... – 2013-05-10 18:29:53

+0

这是一个很好的调试解决方案。谢谢 :) – 2013-05-10 23:30:07

0

你是否依赖垃圾回收器来关闭你的文件? I.E.句柄超出了范围,即使你已经“关闭”了文件,在GC运行之前它也不会消失。如果对象链从未超出范围,则GC无法收集它。另外,如果GC没有机会运行,它们也不会被收集。

我遇到了长时间运行的进程中的相同问题,并通过重新设计我的系统来“解决”它,使得所有文件访问都发生在子对象内部。该对象在完成使用后从参考链中删除,或者发生了一些错误。这允许GC收集手柄。