2013-05-10 63 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收集手柄。

相關問題