Python 踩坑之旅进程篇其五:打不开的新文件问题如何解决

1.1 踩坑案例

长期运行的 daemon 进程或者 socket 测试类进程, 经常遇到的坑是:

IOError: [Errno 24] Too many open files

即进程遇到 IO 错误, 无法打开更多的文件.

1.2 填坑和分析

一般从两个方面入手:

1.2.1 从程序优化入手

  • 检查文件打开是否遵循了"谁打开谁关闭"原则
  • 文件是否存在关闭泄露

a. 谁打开谁关闭是个普适的原则:

  • 只有逻辑设计者自己最熟悉

    • 哪些文件 FD 需要一直维持打开状态
    • 哪些文件直到某个事件发生后关闭
  • 短暂的文件读写打开推荐使用 pythonic 的 with statement

# with 语法会在生命周期后自动关闭打开的文件 FD
with open('xxxx_path.file', 'w') as fhandle:
    fhandle.dosth()

b. 检查文件 FD 是否存在泄漏

系统设计阶段一般会预估系统总体可打开的 FD 情况. 当出现如下情况时可能出现了泄漏 BUG

  • 外围监控系统发现该进程 FD 大量突破了设计预估
  • 打开 FD 增长趋势异常
    • 一般随着业务增加, FD 会线性增长, 但有限度和规律
    • 如果增长曲线不停的出现陡峭增长且在业务低峰期也如此可能出现了泄露

Python 基础库 CUP 提供对进程打开 FD 的支持, 详见示例代码.

1.2.2 从资源软硬限入手

  • 了解系统的资源软硬限制
  • 检查进程可打开的 FD 是否突破了系统限制
    • 长期运行的 daemon 进程尤其注意

Centos 6.3 Linux 系统为例, 查看 /etc/security/limits.conf 获得系统软硬限资源

* soft nofile 10240
* hard nofile 10240

其中, 用户不能突破系统的硬线 hard nofile limit.

用户也可以通过 shell 命令 ulimit -n 来限定该 shell 启动的所有进程的 nofile

  • 当然非 root 用户是不能突破系统硬线的
  • 用户为了进程控制, 可以设定 nofile 更小

ulimit -a 可以查看当前用户被设定的限制, 示例:

[test[@agent1](/user/agent1) ~]$ ulimit -a
core file size          (blocks, -c) 0
.......
open files                      (-n) 10240
.....
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

1.4.1 技术关键字

  • Open FD
  • 资源 Soft limit / Hard limit

下期坑位预告

  • PyDaemon 进程长什么样

文章来源

作者持续更新的 cnblogs: https://www.cnblogs.com/mythmgn/

作者持续更新的 Github:


Life is short. We use Python.


Python 踩坑之旅进程篇其五:打不开的新文件问题如何解决

6 回复

这个问题我遇到过,典型的文件描述符泄漏。在Python里,你打开文件后如果没正确关闭,系统资源就不会释放,导致后续文件操作失败。

看这个典型错误代码:

def process_files():
    files = []
    for i in range(1000):
        f = open(f'file_{i}.txt', 'w')
        f.write('some data')
        files.append(f)  # 这里忘了关闭文件

正确做法是用with语句自动管理:

def process_files():
    for i in range(1000):
        with open(f'file_{i}.txt', 'w') as f:
            f.write('some data')

如果必须手动管理,确保每个open()都有对应的close()

def process_files():
    files = []
    try:
        for i in range(1000):
            f = open(f'file_{i}.txt', 'w')
            f.write('some data')
            files.append(f)
    finally:
        for f in files:
            f.close()

检查系统限制用ulimit -n,Python里可以用resource模块查看当前打开的文件数。用lsof -p <pid>也能看到进程打开了哪些文件。

简单说就是记得关文件。


不错

有的还需要编辑 /etc/systemd/system.conf 的 DefaultLimitNOFILE 和 DefaultLimitNPROC,光靠 /etc/security/limits.conf 无法生效

用什么语言 文件描述符泄露都会导致这个问题 这个真不能算是 python 采坑 另外这本身也不算坑



先赞一个.

是的, 不同发行版配置不一. 我在示例里给了下参考 Centos 6.3. 在文章最后我也提示下大家吧.

代码示例支持
平台: Centos 6.3
Python: 2.7.14
代码示例: 菜单 - Python 踩坑指南代码示例

python3 目前在 beta, 近期会发出来除了 网络通信框架 cup.net.async 之外的版本.

默认 unicode 之后, 对网络包序列化处理上影响多, 这块工作量比想象的大. 哈哈

回到顶部