在 Linux 平台上运行的进程都会从系统资源申请一定数量的句柄,而且系统控制了进程能够申请的最大句柄数量。用户程序如果不及时释放无用的句柄,将会引起句柄泄露,从而可能造成申请资源失败,导致系统文件句柄用光连接不能建立。本文主要介绍Linux下如何查看和修改进程打开的文件句柄数,避免这类问题的发生。
句柄介绍
句柄的介绍及应用
句柄是在 Windows 中引入的一个概念,它是和对象一一对应的 32 位无符号整数值。句柄可以映射到唯一的对象,它是处理对象的一个接口,对于所涉及的对象,可以通过相应的句柄来操作它。句柄的引入主要是操作系统为了避免应用程序直接对某个对象的数据结构进行操作为目的,用操作句柄来代替操作对象。
在 Linux 环境中,任何事物都是用文件来表示,设备是文件,目录是文件,socket 也是文件。用来表示所处理对象的接口和唯一接口就是文件。应用程序在读 / 写一个文件时,首先需要打开这个文件,打开的过程其实质就是在进程与文件之间建立起连接,句柄的作用就是唯一标识此连接。此后对文件的读 / 写时,目标文件就由这个句柄作为代表。最后关闭文件其实就是释放这个句柄的过程,使得进程与文件之间的连接断开。
句柄泄露
造成句柄泄露的主要原因,是进程在调用系统文件之后,没有释放已经打开的文件句柄。在 Linux 系统中,进程与文件之间是通过“打开文件”操作建立连接,文件系统会返回文件句柄来唯一标识进程与文件的连接。每当一个进程执行完毕之后,Linux 系统会将与进程相关的文件句柄自动释放。但是,如果进程一直处于执行状态,文件的句柄只能通过“关闭文件”操作来自我释放。与 Windows 系统的设置不同,Linux 系统对进程可以调用的文件句柄数做了限制,在默认情况下,每个进程可以调用的最大句柄数为 1024 个。超过了这个数值,进程则无法获得新的句柄。因此,句柄的泄露将会对进程的功能失效造成极大的隐患。
如何修改系统最大句柄数
Linux 中,单个进程能够打开的最大文件句柄数量是可以配置的,系统默认是 1024。当单个进程打开的文件句柄数量超过了系统定义的值,就会出现“Too many files open”的错误提示。用户可以通过以下命令查看系统定义的最大值:
ulimit – n
查看当前进程打开了多少句柄数:
$ lsof -n|awk '{print $2}'|sort|uniq -c|sort -nr|more
131 24204
57 24244
57 24231 ......
其中第一列是打开的句柄数,第二列是进程ID。可以根据ID号来查看进程名:
# ps aef|grep 24204
nginx 24204 24162 99 16:15 ? 00:24:25 /usr/local/nginx/sbin/nginx -s
对于一般的应用程序而言 1024 已经完全够用了,但是有些进程处理大量请求,很有可能 1024 就不够用了,则需要调整系统参数,来适应应用的变化。Linux 有硬性和软性设置两种,都可以通过 ulimit 来设置。例如
ulimit – HSn 2048
以上命令就可以设置 H(硬性),S(软性)的值为 2048。n表示设定单个进程最大的打开文件句柄数量。个人觉得最好不要超过4096,毕竟打开的文件句柄数越多响应时间肯定会越慢。设定句柄数量后,系统重启后,又会恢复默认值。如果想永久保存下来,可以修改.bash_profile文件,可以修改 /etc/profile 把上面命令加到最后。
Linux 检测句柄的方法
在 Linux 平台上,lsof(list open files)是一个列出当前系统打开文件的工具。在 Linux 环境下,任何事物都以文件的形式存在,系统在后台为应用程序分配了一个文件描述符,无论这个文件的本质如何,该文件描述符为应用程序与基础操作系统之间的交互提供了通用接口。因为应用程序打开文件的描述符列表提供了大量关于这个应用程序本身的信息,因此通过 lsof 工具能够查看这个列表对系统监测以及排错将是很有帮助的。
在终端下输入 lsof 即可显示系统打开的文件,因为 lsof 需要访问核心内存和各种文件,所以必须以 root 用户的身份运行它才能够充分地发挥其功能。屏幕显示如下:
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
init 1 root cwd DIR 3,2 4096 2 /
init 1 root rtd DIR 3,2 4096 2 /
init 1 root txt REG 3,2 32684 1200637 /sbin/init
……
lsof 输出各列信息的意义如下:
- COMMAND:进程的名称
- PID:进程标识符
- USER:进程所有者
- FD:文件描述符,应用程序通过文件描述符识别该文件。如 cwd、txt 等
- TYPE:文件类型,如 DIR、REG 等
- DEVICE:指定磁盘的名称
- SIZE:文件的大小
- NODE:索引节点(文件在磁盘上的标识)
- NAME:打开文件的确切名称
在 Linux 系统中可以用 man lsof 查看详细的介绍和参数使用方法,在这里不作过多介绍。在侦测程序句柄泄露的应用中,我们主要用到 lsof 的如下使用方法:
lsof – p PID
PID 是指我们要侦测程序的进程号,可以用命令 ps – ef 来得到。我们以进程号 14946 为例:
# lsof -p 14946
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
rpc.rquot 14946 root cwd DIR 3,2 4096 2 /
rpc.rquot 14946 root rtd DIR 3,2 4096 2 /
rpc.rquot 14946 root txt REG 3,2 65292 267543
/usr/sbin/rpc.rquotad
rpc.rquot 14946 root mem REG 3,2 45889 535442
/lib/libnss_files-2.3.4.so
rpc.rquot 14946 root mem REG 3,2 1454802 541622 /lib/tls/
libc-2.3.4.so
……
每一行就代表该进程正在使用的一个文件,即句柄。统计行数总和就是该进程打开的所有句柄数量,这为我们用统计方法侦测句柄泄露提供的依据。
参考资料
Linux下查看进程打开的文件句柄数
侦测程序句柄泄露的统计方法