Обнаружить утечку дескриптора файла в Python? - PullRequest
7 голосов
/ 18 февраля 2009

У моей программы, похоже, есть утечка файловых дескрипторов. Как я могу узнать где?

Моя программа использует файловые дескрипторы в нескольких разных местах - вывод из дочерних процессов, вызов ctypes API (ImageMagick) открывает файлы, и они копируются.

Он падает в shutil.copyfile, но я уверен, что это не то место, где оно течет.

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "C:\Python25\Lib\site-packages\magpy\magpy.py", line 874, in main
    magpy.run_all()
  File "C:\Python25\Lib\site-packages\magpy\magpy.py", line 656, in run_all
    [operation.operate() for operation in operations]
  File "C:\Python25\Lib\site-packages\magpy\magpy.py", line 417, in operate
    output_file = self.place_image(output_file)
  File "C:\Python25\Lib\site-packages\magpy\magpy.py", line 336, in place_image
    shutil.copyfile(str(input_file), str(self.full_filename))
  File "C:\Python25\Lib\shutil.py", line 47, in copyfile
    fdst = open(dst, 'wb')
IOError: [Errno 24] Too many open files: 'C:\\Documents and Settings\\stuart.axon\\Desktop\\calzone\\output\\wwtbam4\\Nokia_NCD\\nl\\icon_42x42_V000.png'
Press any key to continue . . .

Ответы [ 5 ]

4 голосов
/ 20 мая 2014

У меня были похожие проблемы: не хватало файловых дескрипторов во время вызовов subprocess.Popen (). Я использовал следующий скрипт для отладки происходящего:

import os
import stat

_fd_types = (
    ('REG', stat.S_ISREG),
    ('FIFO', stat.S_ISFIFO),
    ('DIR', stat.S_ISDIR),
    ('CHR', stat.S_ISCHR),
    ('BLK', stat.S_ISBLK),
    ('LNK', stat.S_ISLNK),
    ('SOCK', stat.S_ISSOCK)
)

def fd_table_status():
    result = []
    for fd in range(100):
        try:
            s = os.fstat(fd)
        except:
            continue
        for fd_type, func in _fd_types:
            if func(s.st_mode):
                break
        else:
            fd_type = str(s.st_mode)
        result.append((fd, fd_type))
    return result

def fd_table_status_logify(fd_table_result):
    return ('Open file handles: ' +
            ', '.join(['{0}: {1}'.format(*i) for i in fd_table_result]))

def fd_table_status_str():
    return fd_table_status_logify(fd_table_status())

if __name__=='__main__':
    print fd_table_status_str()

Вы можете импортировать этот модуль и вызвать fd_table_status_str(), чтобы записать состояние таблицы дескрипторов файлов в разных точках вашего кода.

Также убедитесь, что экземпляры subprocess.Popen уничтожены. Хранение ссылок на экземпляры Popen в Windows не позволяет запустить GC. И если экземпляры сохраняются, связанные каналы не закрываются. Подробнее здесь .

3 голосов
/ 18 февраля 2009

Используйте Process Explorer , выберите свой процесс, View-> View в нижней панели-> Handles - затем найдите то, что кажется неуместным - обычно множество одинаковых или похожих файлов открывают проблемы.

3 голосов
/ 20 декабря 2010

lsof -p <process_id> хорошо работает на нескольких UNIX-подобных системах, включая FreeBSD.

3 голосов
/ 18 февраля 2009

Посмотрите на вывод из ls -l /proc/$pid/fd/ (конечно, подставляя PID вашего процесса), чтобы увидеть, какие файлы открыты [или, на win32, используйте Process Explorer для просмотра списка открытых файлов]; затем выясните, где в вашем коде вы их открываете, и убедитесь, что вызывается close(). (Да, сборщик мусора в конечном итоге закроет вещи, но это не всегда достаточно быстро, чтобы избежать исчерпания fds).

Проверка на наличие циклических ссылок, которые могут препятствовать сбору мусора, также является хорошей практикой. (Сборщик циклов в конечном итоге избавится от них - но он может работать недостаточно часто, чтобы избежать исчерпания дескриптора файла; меня это укусило лично).

0 голосов
/ 24 мая 2018

Хотя у ОП есть система Windows, я уверен, что многие здесь (например, я) тоже ищут других (это даже не помеченная Windows).

У Google есть пакет psutil с методом get_open_files(). Он выглядит как отличный интерфейс, но, кажется, его не поддерживали в течение пары лет. Я на самом деле написал реализацию для моего собственного проекта Python 2 для Linux. Я использую его с unittest, чтобы мои функции очищали свои ресурсы.

import os

# calling this **synchronously** will accurately relay open files on Linux
def get_open_files(pid):
    # directory spawned by Python process, containing its file descriptors
    path = "/proc/%d/fd" % pid
    # list the abspaths belonging to that directory
    links = ["%s/%s" % (path, f) for f in os.listdir(path)]
    # filter out the bad ones returned by os.listdir()
    valid_links = filter(lambda f: os.path.exists(f), links)
    # these links are fd integers, so map them to their actual file devices
    devices = map(lambda f: os.readlink(f), valid_links)
    # remove any ones that are stdin, stdout, stderr, etc.
    return filter(lambda f: "/dev/pts" not in f, devices)
...