7

我的程序似乎正在泄漏文件句柄。我怎样才能知道在哪里?

我的程序在几个不同的地方使用文件句柄——子进程的输出,调用ctypesAPI (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 . . .
4

6 回答 6

4

我有类似的问题,在 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 实例被销毁。在 Windows 中保留对 Popen 实例的引用会阻止 GC 运行。如果保留实例,则关联的管道不会关闭。更多信息在这里

于 2014-05-20T14:22:46.747 回答
3

查看输出ls -l /proc/$pid/fd/(当然要替换您进程的 PID)以查看打开了哪些文件 [或者,在 win32 上,使用Process Explorer列出打开的文件];然后找出你在代码中打开它们的位置,并确保它close()被调用。(是的,垃圾收集器最终会关闭事物,但它并不总是足够快以避免耗尽 fds)。

检查任何可能阻止垃圾收集的循环引用也是一个好习惯。(循环收集器最终会处理掉这些——但它可能不会足够频繁地运行以避免文件描述符耗尽;我个人对此感到很痛苦)。

于 2009-02-18T17:12:26.283 回答
3

使用Process Explorer,选择您的进程, View->Lower Pane View->Handles - 然后寻找看起来不合适的地方 - 通常打开许多相同或相似的文件指向问题。

于 2009-02-18T17:28:47.040 回答
3

lsof -p <process_id>在包括 FreeBSD 在内的几个类 UNIX 系统上运行良好。

于 2010-12-19T23:01:21.787 回答
0

虽然 OP 有一个 Windows 系统,但我相信这里的很多人(比如我自己)也在寻找其他人(它甚至没有标记为 Windows)。

谷歌有一个psutil带有get_open_files()方法的包。它看起来像一个出色的界面,但它似乎已经有几年没有维护了。实际上,我在 Linux 上为我自己的 Python 2 项目编写了一个实现。我使用它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)
于 2018-05-24T15:52:06.217 回答
0

Python 自己的测试套件有一个使用fd_count的refleak 模块。跨操作系统工作,可在完整安装中使用:

>>> from test.support.os_helper import fd_count
>>> fd_count()
27

在 Python 3.9 及更早版本上,os_helper不存在,所以from test.support import fd_count.

于 2021-07-30T19:29:53.170 回答