Linux下Python3统计进程打开文件描述符的高效方法
Linux下Python3高效统计进程打开文件描述符(FD)的方法
问题背景
使用libzmq的Python服务器存在FD泄漏问题(源于libzmq内部竞态条件,短期无法修复),需要进程自行检测FD数量,当累积到约500个时自动重启,无需外部监控干预。
高效FD计数方案
1. 读取/proc/self/stat获取精确计数(最优)
Linux系统中,/proc/self/stat文件的第24个字段(索引从0开始为第23位)直接记录了当前进程打开的FD总数,无需遍历目录,速度极快。
import os def get_open_fds(): with open('/proc/self/stat', 'r') as stat_file: stat_data = stat_file.read().split() return int(stat_data[23])
2. 用fcntl快速估算FD数量(次优)
通过fcntl.F_DUPFD_CLOEXEC系统调用尝试复制一个高编号的FD,返回的第一个可用FD值减1即为当前已用的最大FD编号。该方法无文件IO,速度最快,但结果是估算值(可能存在FD空洞,比如关闭了中间的FD),适合仅需判断阈值的场景。
import fcntl import os def estimate_open_fds(): try: # 从1024开始尝试复制FD,返回第一个可用的FD new_fd = fcntl.fcntl(0, fcntl.F_DUPFD_CLOEXEC, 1024) os.close(new_fd) return new_fd - 1 except OSError as e: if e.errno == 24: # FD耗尽(EMFILE) return os.getrlimit(os.RLIMIT_NOFILE)[0] raise
3. 优化版遍历/proc/self/fd(精确但稍慢)
若必须精确计数且不想依赖/proc/self/stat,用os.scandir替代os.listdir遍历目录,os.scandir比传统遍历更高效,因为它无需额外调用stat获取条目信息。
import os def count_open_fds(): count = 0 with os.scandir('/proc/self/fd') as entries: for _ in entries: count += 1 return count
自动重启实现示例
结合FD计数逻辑,实现进程自动重启:
import os import sys import time def get_open_fds(): with open('/proc/self/stat', 'r') as stat_file: return int(stat_file.read().split()[23]) def main(): while True: # 这里替换为你的服务器业务逻辑 print("Server running...") # 定期检查FD数量(示例为每60秒检查一次) fd_count = get_open_fds() if fd_count >= 500: print(f"FD count reached {fd_count}, initiating restart...") # 用execv重启进程,替换当前进程空间,避免僵尸进程 os.execv(sys.executable, [sys.executable] + sys.argv) time.sleep(60) if __name__ == "__main__": main()
注意事项
- 若允许依赖第三方库,
psutil.Process().num_fds()是跨平台的精确计数方法,但会增加依赖。 - 重启前需确保服务器优雅关闭,比如处理完当前请求、关闭连接,避免数据丢失。
- 阈值(500)需低于系统FD软限制,可通过
os.getrlimit(os.RLIMIT_NOFILE)[0]查看当前软限制。
内容的提问来源于stack exchange,提问作者Goswin von Brederlow
相关产品推荐
相关产品推荐

