近期在使用 python 进行多进程编程,遇到了 too many open files 这个错误,经过分析后,发现是文件描述符的问题。
这里要知道一个概念,在 linux 中,一切都是文件。
先说一下这个情况是怎么发生的。
- 项目是一个股票分析中间件
- 通过接收 redis 集群传递过来的数据
- 将这些股票数据进行分析整合
基于上述的项目要求,我最初的设计架构是这样的
- 主进程接受数据,并且,为每一个股票代码创建一个独立的股票进程
- 然后主进程同时创建一个管道,用以将主进程接受到的数据传递给相关的股票进程
但是,在实际的使用过程中,当股票进程创建到 300 个左右的时候,就会出现 too many open files 这个错误。
这里介绍一下 「文件描述符」。
linux 的文件描述符
文件描述符(FD:file descriptors)
,也可以说是文件句柄,当某个程序打开文件时,内核返回相应的文件描述符,程序为了处理该文件必须引用此描述符。文件描述符是一个正整数,用以标明每一个被进程所打开的文件和 socket。最前面的三个文件描述符(0,1,2)
分别与标准输入(stdin)
,标准输出(stdout)
和标准错误(stderr)
对应,后面打开的文件依此类推对应 3
、4
…… 。
linux
系统对每个用户、进程、或整个系统的可打开文件描述符数量都有一个限制,一般默认为 1024
。当我们在系统或应用的日志中碰到 too many open files
错误记录时,这个其实不是说打开的文件过多,而是打开的文件描述符数量已达到了限制,这时就需要增加文件描述符的数量限制了。
获取系统打开的文件描述符数量
[root@localhost ~]# cat /proc/sys/fs/file-nr
1216 0 197787
- 第一列
1216
:为已分配的FD
数量 - 第二列
0
:为已分配但尚未使用的FD
数量 - 第三列
197787
:为系统可用的最大FD
数量
已用
FD 数量=为已分配的 FD 数量 - 为已分配但尚未使用的 FD 数量
注意,这些数值是系统层面的。
获取进程打开的文件描述符数量
[root@localhost ~]# pidof vim
3253
[root@localhost ~]# ll /proc/3253/fd
lrwx------. 1 test test 64 6月 8 18:11 0 -> /dev/pts/0
lrwx------. 1 test test 64 6月 8 18:11 1 -> /dev/pts/0
lrwx------. 1 test test 64 6月 8 18:11 2 -> /dev/pts/0
lrwx------. 1 test test 64 6月 8 18:11 4 -> /home/test/.bash_history.swp
可以看到 vim
进程用了 4
个 FD
更改文件描述符限制
当碰到 too many open files
错误时,就需要增加文件描述符的限制数量,系统的默认文件描述符都比较大,一般来说,只需增加用户或进程的就可以了
用户或进程
[root@localhost ~]# ulimit -n
1024
[root@localhost ~]# ulimit -n 10240
[root@localhost ~]# ulimit -n
10240
注意,使用 ulimit
命令更改后只是在当前会话生效,当退出当前会话重新登录后又会回到默认值 1024
,要永久更改可以修改文件 /etc/security/limit.conf
,如
[root@localhost ~]#vi /etc/security/limits.conf
加入 “abc hard nofile 10240”
abc
:用户名,即允许test
使用ulimit
命令更改FD
限制,最大值不超过10240
,更改后abc
用户的每一个进程(以abc
用户运行的进程)可打开的FD
数量为10240
个hard
:限制类型,有soft/hard
两种,达到soft
限制会在系统的日志(一般为/var/log/messages
)里面记录一条告警日志,但不影响使用。hard
,达到这个限制,有日志且会影响使用。nofile
:限制的内容,nofile - max number of open files
1024
:值
更改后,退出终端重新登录,用 ulimit
看看有没有生效,如果没生效,可以在 abc
用户的.bash_profile
文件加上 ulimit -n 10240
,以使用户 abc
每次登录时都会将 FD
最大值更改为 10240
,如:
[root@localhost ~]#echo "ulimit -n 10240" >> /home/abc/ .bash_profile
10240
[root@localhost ~]# su - abc
[abc@localhost ~]\$ ulimit -n
10240
limit.conf
文件里面本身已有很详细的使用方法,这个下次可以说说。
系统级别
将整个操作系统可以使用的 FD
数量更改为 51200
[root@localhost ~]# echo "51200" > /proc/sys/fs/file-max
[root@localhost ~]# cat /proc/sys/fs/file-nr
1184 0 51200
像这样更改在系统重启后会恢复到默认值,要永久更改可以在 sysctl.conf
文件加上 fs.file-max = 51200
如:
[root@localhost ~]# echo "fs.file-max = 51200" >> /etc/sysctl.conf
获取打开的文件数量
linux
的一切皆为文件,那么如何知道系统/应用打开了哪些或是多少个文件呢?很简单,用 lsof
命令就行了,lsof
,list open files
的简写,可列出程序或系统正在使用的文件。
获取整个系统打开的文件数量
[root@localhost ~]# lsof |wc -l
1864
获取某个用户打开的文件数量
[root@localhost ~]# lsof -u test |wc -l
15
获取某个程序打开的文件数量
[root@localhost ~]# pidof vim
3253
[root@localhost ~]# lsof -p 3253 |wc -l
31
上面所示只是用 lsof
来显示已打开的文件数量,lsof
的功能远不止这些,有兴趣可以 man lsof
看一下
解决方案
按照上述的方法,我的检查步骤如下
pidof python
发现,python
程序大概有 300
个左右,然后选取一个 python
的 PID
进行
ll /proc/3253/fd | wc -l
发现每个 python
程序中有 几百个。这就让人感到困惑,因为,按照理论上来说,我只是开启了
- 一个管道
- 一个进程
而已,怎么一个程序会打开几百个文件描述符呢?
这个问题,目前,我还没有解决,等以后更新吧。
这里可以放一个运行程序,可以运行一下,就会出现上面的错误。
1 | import os |