проверьте, какие файлы открыты в Python - PullRequest
46 голосов
/ 07 января 2010

Я получаю сообщение об ошибке в программе, которая должна работать в течение длительного времени, если открыто слишком много файлов. Можно ли как-то отслеживать, какие файлы открыты, чтобы я мог время от времени распечатывать этот список и видеть, в чем проблема?

Ответы [ 11 ]

37 голосов
/ 01 августа 2014

Чтобы перечислить все открытые файлы в кросс-платформенном режиме, я бы порекомендовал psutil .

#!/usr/bin/env python
import psutil

for proc in psutil.process_iter():
    print proc.open_files()

Исходный вопрос неявно ограничивает операцию текущим процессом, к которому можно обратиться через класс Process в psutil.

proc = psutil.Process()
print proc.open_files()

Наконец, вы захотите запустить код, используя учетную запись с соответствующими разрешениями для доступа к этой информации, или вы можете увидеть ошибки AccessDenied.

37 голосов
/ 08 января 2010

Я закончил оборачивать встроенный объект файла в точке входа моей программы. Я узнал, что я не закрывал свои регистраторы.

import io
import sys
import builtins
import traceback
from functools import wraps


def opener(old_open):
    @wraps(old_open)
    def tracking_open(*args, **kw):
        file = old_open(*args, **kw)

        old_close = file.close
        @wraps(old_close)
        def close():
            old_close()
            open_files.remove(file)
        file.close = close
        file.stack = traceback.extract_stack()

        open_files.add(file)
        return file
    return tracking_open


def print_open_files():
    print(f'### {len(open_files)} OPEN FILES: [{", ".join(f.name for f in open_files)}]', file=sys.stderr)
    for file in open_files:
        print(f'Open file {file.name}:\n{"".join(traceback.format_list(file.stack))}', file=sys.stderr)


open_files = set()
io.open = opener(io.open)
builtins.open = opener(builtins.open)
23 голосов
/ 08 января 2010

В Linux вы можете посмотреть содержимое /proc/self/fd:

$ ls -l /proc/self/fd/
total 0
lrwx------ 1 foo users 64 Jan  7 15:15 0 -> /dev/pts/3
lrwx------ 1 foo users 64 Jan  7 15:15 1 -> /dev/pts/3
lrwx------ 1 foo users 64 Jan  7 15:15 2 -> /dev/pts/3
lr-x------ 1 foo users 64 Jan  7 15:15 3 -> /proc/9527/fd
14 голосов
/ 22 августа 2011

Хотя вышеописанные решения полезны для собственного кода, я отлаживал свой клиент в сторонней библиотеке, включающей некоторый код расширения c, поэтому мне нужен был более прямой путь. Следующая процедура работает под darwin и (я надеюсь) в других unix-подобных средах:

def get_open_fds():
    '''
    return the number of open file descriptors for current process

    .. warning: will only work on UNIX-like os-es.
    '''
    import subprocess
    import os

    pid = os.getpid()
    procs = subprocess.check_output( 
        [ "lsof", '-w', '-Ff', "-p", str( pid ) ] )

    nprocs = len( 
        filter( 
            lambda s: s and s[ 0 ] == 'f' and s[1: ].isdigit(),
            procs.split( '\n' ) )
        )
    return nprocs

Если кто-нибудь сможет расширить возможности переноса на Windows, я был бы благодарен.

9 голосов
/ 08 января 2010

В Linux вы можете использовать lsof для отображения всех файлов, открытых процессом.

5 голосов
/ 08 января 2010

В Windows вы можете использовать Process Explorer , чтобы показать все файловые дескрипторы, принадлежащие процессу.

3 голосов
/ 17 июля 2014

Как уже говорилось ранее, вы можете перечислить fds в Linux в / proc / self / fd , вот простой способ для их программного перечисления:

import os
import sys
import errno

def list_fds():
    """List process currently open FDs and their target """
    if sys.platform != 'linux2':
        raise NotImplementedError('Unsupported platform: %s' % sys.platform)

    ret = {}
    base = '/proc/self/fd'
    for num in os.listdir(base):
        path = None
        try:
            path = os.readlink(os.path.join(base, num))
        except OSError as err:
            # Last FD is always the "listdir" one (which may be closed)
            if err.errno != errno.ENOENT:
                raise
        ret[int(num)] = path

    return ret
3 голосов
/ 21 мая 2013

Существуют некоторые ограничения для принятого ответа в том смысле, что он не учитывает количество каналов. У меня был скрипт на python, который открывал много подпроцессов и не мог должным образом закрыть стандартные каналы ввода, вывода и ошибок, которые использовались для связи. Если я использую принятый ответ, он не сможет посчитать эти открытые каналы как открытые файлы, но (по крайней мере, в Linux) они являются открытыми файлами и учитывают ограничение на количество открытых файлов. Решение lsof -p, предложенное sumid и shunc, работает в этой ситуации, потому что оно также показывает открытые каналы.

2 голосов
/ 19 декабря 2013

Получить список всех открытых файлов. handle.exe является частью Sysinternals Suite от Microsoft . Альтернативой является модуль Python psutil, но я считаю, что «handle» распечатает больше используемых файлов.

Вот что я сделал. Код клудги предупреждение.

#!/bin/python3
# coding: utf-8
"""Build set of files that are in-use by processes.
   Requires 'handle.exe' from Microsoft SysInternals Suite.
   This seems to give a more complete list than using the psutil module.
"""

from collections import OrderedDict
import os
import re
import subprocess

# Path to handle executable
handle = "E:/Installers and ZIPs/Utility/Sysinternalssuite/handle.exe"

# Get output string from 'handle'
handle_str = subprocess.check_output([handle]).decode(encoding='ASCII')

""" Build list of lists.
    1. Split string output, using '-' * 78 as section breaks.
    2. Ignore first section, because it is executable version info.
    3. Turn list of strings into a list of lists, ignoring first item (it's empty).
"""
work_list = [x.splitlines()[1:] for x in handle_str.split(sep='-' * 78)[1:]]

""" Build OrderedDict of pid information.
    pid_dict['pid_num'] = ['pid_name','open_file_1','open_file_2', ...]
"""
pid_dict = OrderedDict()
re1 = re.compile("(.*?\.exe) pid: ([0-9]+)")  # pid name, pid number
re2 = re.compile(".*File.*\s\s\s(.*)")  # File name
for x_list in work_list:
    key = ''
    file_values = []
    m1 = re1.match(x_list[0])
    if m1:
        key = m1.group(2)
#        file_values.append(m1.group(1))  # pid name first item in list

    for y_strings in x_list:
        m2 = re2.match(y_strings)
        if m2:
            file_values.append(m2.group(1))
    pid_dict[key] = file_values

# Make a set of all the open files
values = []
for v in pid_dict.values():
    values.extend(v)
files_open = sorted(set(values))

txt_file = os.path.join(os.getenv('TEMP'), 'lsof_handle_files')

with open(txt_file, 'w') as fd:
    for a in sorted(files_open):
        fd.write(a + '\n')
subprocess.call(['notepad', txt_file])
os.remove(txt_file)
2 голосов
/ 08 января 2010

Я предполагаю, что у вас есть утечка файловых дескрипторов. Возможно, вы захотите просмотреть свой код, чтобы убедиться, что закрываете все файлы, которые открываете.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...