測試Linux最大打開文件數參數

基礎概念

打開文件數,如字面意思,指的是打開文件的數量。mysql

 

之前,我一直在想,"打開文件"是一個什麼概念。後來,學了一點C語言,才明白,程序訪問一個文件時是須要先打開文件的。體如今C語言編程中,就是程序會使用函數,如fopen( )函數,來打開該文件。好比,程序要將日誌寫入到/root/test.log文件中,就可能會使用 fopen("/root/test.log", "w") 來打開該文件,後面的w則限定了程序只能對該文件進行寫入操做,而且程序會先將文件內容清空(若是文件不存在就會先建立文件),相似的還有"r" (只讀)、"a+" (可讀可寫)等。程序打開文件後,才能進行讀取文件內容或寫入內容到文件等操做。當程序不使用某一個文件時,還要使用fclose( )函數來關閉文件。nginx

 

所以來講,當程序打開一個文件時,就會產生1個打開文件數。程序打開幾個文件就會產生幾個打開文件數。而操做系統對程序所能打開的文件數量是有限制的。操做系統爲何要限制呢?由於打開文件是須要消耗資源的,操做系統須要追蹤記錄哪些程序打開了哪些文件,而且有些文件的內容可能須要讀入內存。因此操做系統會限制程序的"最大打開文件數"。web

 

儘管來講,程序打開文件時會消耗系統資源,操做系統也會限制最大打開文件數,但那又有什麼關係呢?一般來講,Web服務器、數據庫服務器等,若是都沒什麼訪問量的話,咱們固然就沒有必要關注打開文件數了。可是,訪問量稍大一點時,就要必要了。特別是在RHEL/CentOS 6等有點年代的系統中。sql

 

查看及修改最大打開文件數

在RHEL/CentOS系列的操做系統中,最大打開文件數限制有軟限制(soft limit)和硬限制(hard limit)之分。shell

 

一般來講,軟限制值就是程序的最大打開文件數限值,在RHEL/CentOS 6中這個值默認是1024,程序打開的全部文件數量不能超過這個值。使用ulimit -n命令能夠查看當前的軟限制值:數據庫

[tuser@gw ~]$ ulimit -n編程

1024vim

可是,普通用戶也能夠將這個值調大。使用 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                                                             #執行程序

 

上面的程序簡單改動一下,經屢次測試,能夠獲得以下結論:

一、 在操做系統中,最大打開文件數有soft限值和hard限值之分,而soft <= hard。測試發現,soft限值決定了打開文件數的限值,而hard限值只是決定了soft限值的最大值而已。實際的打開文件數絕對不會超過soft限值的限制。

二、 雖然,在操做系統中的/etc/security/limits.conf文件中,咱們是分用戶來設定最大打開文件數限值的,據此,不一樣的用戶能夠有不一樣的最大打開文件數限值。可是,測試發現,打實是開文件數限制其限制該用戶單個進程的最大打開文件數,而不是限制該用戶全部進程的總的打開文件數。以普通用戶默認限值1024爲例,屬於該普通用戶的任意一個進程的最大打開文件數都不可能超過1024,可是該普通用戶的全部進程加起來的總的打開文件數並無限制,好比總的打開文件數多是10000或更多。

三、 即使是同一個程序,屢次打開同一個文件,打開文件數也會相應增長,並不會被看做只有一個打開文件數。

四、 查看進程的打開文件數可使用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命令。

 

其它問題

 

一、網絡鏈接是否會佔用打開文件數?

會,一個listening或established狀態的網絡鏈接會佔用一個打開文件數。因此,在web應用的訪問量稍大時,若是是單進程程序的話,即使不算應用自己打開的常規文件,因爲網絡鏈接數多,也會致使打開文件數輕輕鬆鬆就超過1024個。因此,對於CentOS/RedHat 6這種老系統來講,因爲默認值比較小,因此是頗有必要調整的。

根據man文檔中的說法,一個打開文件多是一個常規文件、一個目錄、一個塊設備文件、一個字符設備文件、一個正在執行的文件引用、一個庫、一個流或一個網絡文件(網絡socket,NFS文件或UNIX socket)。因此,網絡鏈接也算。我估計,這多是由於在程序中,要訪問這些對象時,都有點相似於訪問文件那樣,須要打開。

 

二、當你修改了/etc/security/limits.conf文件中的ulimit限值(包括打開文件數)後,是否須要重啓正在運行的程序?

是。由於ulimit限值是跟你當前的shell綁定的,你在哪一個shell裏面啓動了程序,若是程序自己沒有修改ulimit限值的話,程序就會繼承那個shell環境的ulimit限值。因此,一般修改limits.conf文件中的限值後,要退出當前shell並從新登陸,讓新的限值生效,再重啓你的程序。

固然,正如我前面所說,要查看一個進程運行後實際生效的ulimit限值,使用cat /proc/pid/limits命令。若是程序自身有修改ulimit限值的話,你就會看到它的實際限值與你當前shell環境的限值是不同的。

 

三、是否修改了/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其實也是支持的。

相關文章
相關標籤/搜索