码迷,mamicode.com
首页 > 系统相关 > 详细

测试Linux最大打开文件数参数

时间:2018-07-06 23:29:43      阅读:217      评论:0      收藏:0      [点我收藏+]

标签:切换   out   分享   写入   因此   sprintf   说明   temp   socket   

基础概念

打开文件数,如字面意思,指的是打开文件的数量。

 

以前,我一直在想,"打开文件"是一个什么概念。后来,学了一点C语言,才明白,程序访问一个文件时是需要先打开文件的。体现在C语言编程中,就是程序会使用函数,如fopen( )函数,来打开该文件。比如,程序要将日志写入到/root/test.log文件中,就可能会使用 fopen("/root/test.log", "w") 来打开该文件,后面的w则限定了程序只能对该文件进行写入操作,并且程序会先将文件内容清空(如果文件不存在就会先创建文件),类似的还有"r" (只读)、"a+" (可读可写)等。程序打开文件后,才能进行读取文件内容或写入内容到文件等操作。当程序不使用某一个文件时,还要使用fclose( )函数来关闭文件。

 

因此来说,当程序打开一个文件时,就会产生1个打开文件数。程序打开几个文件就会产生几个打开文件数。而操作系统对程序所能打开的文件数量是有限制的。操作系统为什么要限制呢?因为打开文件是需要消耗资源的,操作系统需要追踪记录哪些程序打开了哪些文件,并且有些文件的内容可能需要读入内存。所以操作系统会限制程序的"最大打开文件数"。

 

尽管来说,程序打开文件时会消耗系统资源,操作系统也会限制最大打开文件数,但那又有什么关系呢?通常来说,Web服务器、数据库服务器等,如果都没什么访问量的话,我们当然就没有必要关注打开文件数了。但是,访问量稍大一点时,就要必要了。特别是在RHEL/CentOS 6等有点年代的系统中。

 

查看及修改最大打开文件数

在RHEL/CentOS系列的操作系统中,最大打开文件数限制有软限制(soft limit)和硬限制(hard limit)之分。

 

通常来说,软限制值就是程序的最大打开文件数限值,在RHEL/CentOS 6中这个值默认是1024,程序打开的所有文件数量不能超过这个值。使用ulimit -n命令可以查看当前的软限制值:

[tuser@gw ~]$ ulimit -n

1024

但是,普通用户也可以将这个值调大。使用 ulimit -n number命令将软限制值临时调大或调小。但是,普通用户最多也只能将其调整到硬限制值。

 

硬限制值就是限制用户的软限制值所能调整的最大上限,在RHEL/CentOS 6中这个值默认是4096,也就是说,普通用户自己如果要修改软限制值的话,最大只能修改到4096。硬限制值只有root用户可以修改。使用ulimit -n -H命令可以查看当前的硬限制值:

[tuser@gw ~]$ ulimit -n -H

4096

使用 ulimit -n -H number命令可以调整硬限制值。

 

当然,最好是在/etc/security/limits.conf文件中进行设置,以让其永久生效。怎么在该文件中设置,网上也有很多文章,我就不介绍了。但是,即便在该文件中设置了,也不是对所有情况都生效的,后面我会说到。它只能保证你重新登录,或系统重启后你重新登录,你看到的设置是仍然生效的。

 

进行测试

先创建一个用于测试的用户,查看到它的当前打开文件数限制是1024:

[root@gw ~]# useradd tuser

[root@gw ~]# su - tuser

[tuser@gw ~]$ ulimit -n

1024

然后,再退出来,查看到该用户当前的已打开文件数是0:

[tuser@gw ~]$ exit

logout

[root@gw ~]# lsof -u tuser | wc -l

0

 

重新切换到该用户下,写一个用于测试打开文件数的简单C程序:

[tuser@gw ~]$ vim test_openfiles.c

#include <stdio.h>

#define OPEN_FILES 1025

#define LENGTH 20

#define SECOND 600

int main(void)

{

       int count;

       char array[OPEN_FILES][LENGTH];

       FILE * fp;

       for (count = 0; count < OPEN_FILES; count++)

       {

               sprintf(array[count], "tempdir/%d", count);

               fp = fopen(array[count], "w");

               if ( fp != NULL )

               {

                       printf("Program has opened %d files.\n", count + 1);

               }

               else

               {

                       printf("Program failed when opening the %dth file.\n", count + 1);

               }

       }

       sleep(SECOND);

       return 0;

}

该程序会尝试打开OPEN_FILES指定的文件个数,并输出打开成功与否,然后等待SECOND秒数,再终止。

 

编译:

[tuser@gw ~]$ gcc test_openfiles.c

然后执行:

[tuser@gw ~]$ mkdir tempdir                                                #先建个目录tempdir,用于存放打开的文件

[tuser@gw ~]$ ./a.out                                                             #执行程序

 

上面的程序简单改动一下,经多次测试,可以得到如下结论:

1、 在操作系统中,最大打开文件数有soft限值和hard限值之分,而soft <= hard。测试发现,soft限值决定了打开文件数的限值,而hard限值只是决定了soft限值的最大值而已。实际的打开文件数绝对不会超过soft限值的限制。

2、 虽然,在操作系统中的/etc/security/limits.conf文件中,我们是分用户来设定最大打开文件数限值的,据此,不同的用户可以有不同的最大打开文件数限值。但是,测试发现,打实是开文件数限制其限制该用户单个进程的最大打开文件数,而不是限制该用户所有进程的总的打开文件数。以普通用户默认限值1024为例,属于该普通用户的任意一个进程的最大打开文件数都不可能超过1024,但是该普通用户的所有进程加起来的总的打开文件数并没有限制,比如总的打开文件数可能是10000或更多。

3、 即便是同一个程序,多次打开同一个文件,打开文件数也会相应增加,并不会被看作只有一个打开文件数。

4、 查看进程的打开文件数可以使用lsof命令查看,如(另开一个终端)查看进程8670:

[root@gw ~]# lsof -p 8670

直接使用lsof -p 2961 | wc -l 命令来计算进程的打开文件数并不准确,得到的是一个粗略的值。下面是一个示例输出:

技术分享图片

可以看到,在输出的第四列,其实程序已经告诉我们了每一个文件是第几个打开的文件。由于是从0开始编号的,看图中,编号已经到1023了,所以进程8670现在已经是打开了1024个文件了,当前打开文件数的soft限值也是1024。

 

lsof命令输出的第四列FD (File Descriptor)列的含义:

该列字段的值可能是,文件的文件描述符编号或下图中的之一。如果是文件描述符编号,它后面会跟有一个模式字符(mode character)和一个锁字符(lock character)。

技术分享图片

模式字符表示该文件所处的打开模式,取值可能是下面五种之一:

技术分享图片

锁字符表示应用于该文件的锁的类型,取值可能是下面之一:

技术分享图片

 

当一个进程在运行中时,查看一个进程实际应用的ulimit限值(包括最大打开文件数)的最准确的方式,是查看它的/proc/pid/limits文件。比如,要查看进程8670的应用的ulimit限值,使用命令:

[root@gw ~]# cat /proc/8670/limits

当然,要查看该进程实际打开了多少个文件,则是前面介绍的,使用lsof命令。

 

其它问题

 

1、网络连接是否会占用打开文件数?

会,一个listening或established状态的网络连接会占用一个打开文件数。所以,在web应用的访问量稍大时,如果是单进程程序的话,即便不算应用本身打开的常规文件,由于网络连接数多,也会导致打开文件数轻轻松松就超过1024个。所以,对于CentOS/RedHat 6这种老系统来说,由于默认值比较小,所以是很有必要调整的。

根据man文档中的说法,一个打开文件可能是一个常规文件、一个目录、一个块设备文件、一个字符设备文件、一个正在执行的文件引用、一个库、一个流或一个网络文件(网络socket,NFS文件或UNIX socket)。所以,网络连接也算。我估计,这可能是因为在程序中,要访问这些对象时,都有点类似于访问文件那样,需要打开。

 

2、当你修改了/etc/security/limits.conf文件中的ulimit限值(包括打开文件数)后,是否需要重启正在运行的程序?

是。因为ulimit限值是跟你当前的shell绑定的,你在哪个shell里面启动了程序,如果程序本身没有修改ulimit限值的话,程序就会继承那个shell环境的ulimit限值。所以,通常修改limits.conf文件中的限值后,要退出当前shell并重新登录,让新的限值生效,再重启你的程序。

当然,正如我前面所说,要查看一个进程运行后实际生效的ulimit限值,使用cat /proc/pid/limits命令。如果程序自身有修改ulimit限值的话,你就会看到它的实际限值与你当前shell环境的限值是不一样的。

 

3、是否修改了/etc/security/limits.conf文件中的ulimit限值(包括打开文件数)后,就能保证它对所有的程序生效?

这是错误的。事实上来说,limits.conf文件中的限值对通过启动脚本来启动的程序并不生效。比如,nginx程序有一个启动脚本/etc/init.d/nginx并设置了开机启动。那么,即便你修改了limits.conf文件中的限值,当服务器重启后,nginx程序自动启动了,它的ulimit限值将还会是默认值,而不会是你设置的值。当然,如果你此时登录进系统,并通过nginx开机启动脚本重启了nginx程序,nginx进程的ulimit限值自然会变为你在limits.conf文件中设置的限值。

关于这个问题的原因,我也没有找到什么权威的资料说明,但我估计可能是这样的。以CentOS 6系统为例,因为系统启动时,系统中的所有进程都是由第一支程序/sbin/init带起的。而limits.conf文件中的限值对/sbin/init程序并不生效,所以/sbin/init进程的ulimit限值仍然是默认值。这就导致它所启动的所有子进程,即系统中的所有其它程序,都继承它的ulimit限值,即默认值。

 

对于这个问题,我想到的有两种解决办法。

第一种,是在程序的启动脚本里面最前面加上ulimit修改命令:

[root@gw ~]# vim /etc/init.d/mysql

#!/bin/sh

ulimit -n 65535

第二种,就是,很多程序其实都支持在程序配置文件中修改程序的最大打开文件数,这样就不用管shell环境的ulimit限值是什么了。比如,nginx可以通过worker_rlimit_nofile指令来设置它的worker进程的最大打开文件数。诸如MySQL其实也是支持的。

 

 

 

测试Linux最大打开文件数参数

标签:切换   out   分享   写入   因此   sprintf   说明   temp   socket   

原文地址:http://blog.51cto.com/techsnail/2137383

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!