原理

概念区分

  • 内核维护的打开文件表(System Open File Table)

内核维护着一个打开文件表(看作一个数组),当某个进程打开一个文件时,内核会在这个表中添加一条记录,这个记录是一个结构体,包含了如下数据:文件的偏移量(进程操作(比如读)到文件的哪个位置)等等

一个进程可以打开同一个文件多次,那么就会内核的打开文件表中就会相应地增加多个记录。后文中我们把该表的记录叫做文件句柄

  • 文件描述符(File Descriptor)

上面提到内核维护着一个打开文件表,其实进程也在PCB(Process Control Block)中维护着一个文件描述符表(File Descriptor Table)。当进程打开一个文件时,内核会在打开文件表中新增一条记录,同时进程也会在自已的文件描述符表中添加一条记录,该条记录中有一个指针,指向了内核的打开文件表中对应的记录。

一个进程可以打开同一个文件多次,那么就会在FDT中相应地增加多个记录。后文中我们将用打开文件数来表示进程的文件描述符的个数

Linux的参数调优

我们可能经常会遇到too many open files报错,这是因为“进程的打开文件数(文件描述符)过多,超过了进程的上限(一般都是进程的文件描述符超过限制,目前还没遇到文件句柄超出)”。所以,我们需要对进程的打开文件数上限做调整。

用户

如果没有对进程做显示的设置,进程的打开文件数上限会继承该进程所属的用户的打开文件数上限(正确的表述未找到,可以理解为该用户下单个进程的打开文件数上限)。

我们可以通过以下的方法设置用户的打开文件数上限

$ vim /etc/security/limits.conf
...
<user> - nofile 65535
...

我们也可以通过* - nofile 65535来设置所有用户(*代表所有用户)的打开文件数上限。设置好之后我们可以通过命令ulimit -n来查看当前用户的打开文件数上限。

其实,打开文件数上限有分Soft LimitHard Limit。上面的设置方法中第二列为-,表示同时设置Soft LimitHard Limit。当然,我们也可以通过如下方法来设置用户的Soft LimitHard Limit,需要注意的是Soft Limit不能超过Hard Limit

命令ulimit -n查看的其实是Soft Limit。可以通过命令ulimit -Sn查看Soft Limit,通过命令ulimit -Hn查看Hard Limit

系统的默认配置中,每个用户的Soft Limit为1024,Hard Limit为2048

Tips: 建议将用户的Soft LimitHard Limit设为一样,且所有用户都设大一些为65535

这样再说一点实用的小技巧,如果想查看其他用户的最大打开文件数,但是却不知道那个用户的密码。那么可以这样做:首先新建文件 ulimit.sh,内容如下

然后为ulimit.sh添加可执行属性,然后执行

进程

我们设置特定进程的打开文件数上限,但是注意进程的打开文件数不能超过用户的打开文件数

对于systemd管理的进程,我们可以通过下面的方法同时设置Soft LimitHard Limit

可以通过cat /proc/<pid>/limits查看进程的打开文件数上限,如下Max open files一行

可以通过命令ls -l /proc/<pid>/fd | wc -l统计进程已经打开的文件数(文件描述符数)

Linux下的指标

  • /proc/sys/fs/file-max

该指标的官方定义为(官方链接待补充)

The value in file-max denotes the maximum number of file handles that the Linux kernel will allocate. When you get a lot of error messages about running out of file handles, you might want to raise this limit. The default value is 10% of RAM in kilobytes. To change it, just write the new number into the file

也就是说file-max指的是内核中文件句柄数的最大值

  • /proc/sys/fs/file-nr

其中最后一个参数就是file-max的值,第一个表示已经打开的文件句柄数,(第二个参数暂时还未清楚,好像一直为0

  • /proc/<pid>/fd

该目录下有很多以数字命名的链接文件,这些数字,其实就是进程的文件描述符表中的索引,即数组的索引。所以经常也把文件描述符看成是一个数字。 进程每打开一个文件,文件描述符就会加1。这个目录下看到的文件描述符不一定连续,因为有些文件可能已经关闭了。

  • /proc/<pid>/limits

其中有一行叫Max open files,这里的open files其实就是进程的文件描述符的上限。不过注意有分Soft LimitHard Limit,我们可以为进程设置这两个值,但是Soft Limit必须小于等于Hard Limit(设置方法见《Nofile》)。如果没有给进程设置,则会继承该进程所属用户的配置。执行命令ulimit -Sn可以查看用户的Soft Limit,执行命令ulimit -Hn可以查看用户的Hard Limit

实践

接下来,我们写一个程序,打开一个文件一千次,然后查看内核的打开文件数与进程的文件描述符

执行前,我们先看一下内核的打开文件表,有6496个

然后运行该程序

接着,我们再来查看内核的打开文件表,为7488,约增加了1000个(因为有别的进程会打开或关闭文件)

我们查看该进程的id,为13196

然后查看进程的文件描述符,有1004个

其中012是每个进程都有的,代表stdinstdoutstderr,我们来查看3,如下

Reference

  • https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/5/html/tuning_and_optimizing_red_hat_enterprise_linux_for_oracle_9i_and_10g_databases/chap-oracle_9i_and_10g_tuning_guide-setting_file_handles

  • https://www.ibm.com/support/knowledgecenter/en/ssw_aix_71/com.ibm.aix.genprogc/using_file_descriptors.htm

  • http://www.opstool.com/article/166

  • https://blog.csdn.net/lizhidefengzi/article/details/71481195

Last updated

Was this helpful?