天天看點

linux打開最大檔案數

作者:碼農世界

我們在日常維護中,常遇到"too many open file"的錯誤,有的系統,比如ES,要求啟動時候擴大打開檔案描述符的個數,不如會有如下的提示:

[1]: max file descriptors [4096] for elasticsearch process likely too low, increase to at least [65536]
           

是以搞清楚檔案描述符很重要,在限制檔案描述符的時候,也常有這樣的困惑,限制是對這個使用者來講的,還是對單個程序,還是對整個系統那,如果盲目擴大,可能引起一些資源耗盡性質的攻擊。我對這些概念的了解也不是十厘清楚,是以查閱了網際網路上的資料和自己測試,才寫了這篇文章,希望能對大家了解linux的檔案描述符,有所幫助。

一 ulimit

ulimit 其實是限制shell,以及shell啟動程序的使用資源情況,這樣看起來ulimit限制是程序級别,我們可以通過ulimit指令友善地臨時修改限制值。 比如我們修改打開檔案數量:

ulimit -n 100
           

這樣設定後,我們可以打開最多100個檔案句柄數(這裡面也是描述符數),測試如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>


int main(int argc,char * argv[])
{

   int i = 0;
   for (i = 0; i < 101; i++) {
      printf("open %d file.\n",i);
     // 打開檔案會同時增加檔案描述符和檔案句柄數量
      int f = open("/dev/zero",O_RDONLY);
      if (f > 0) {
         printf("open OK:%d \n",f);
      } else {
         printf("open error.\n");
      }
   }
}
           

用這樣的程式跑以下,會發現在兩個不同的終端,都最多可以打開97個檔案描述符(說明限制是程序級别):

open 0 file.
open OK:3 
open 1 file.
...
...
open OK:97 
open 95 file.
open OK:98 
open 96 file.
open OK:99 
open 97 file.
open error.
open 98 file.
open error.
open 99 file.
open error.
open 100 file.
open error.
           

為什麼不是100個,是因為程式預設打開了标準輸入,标準輸出和标準出錯三檔案描述符,是以隻剩下了97個檔案描述符,而且是從3開始的,也驗證了,打開檔案從最小的未用的整數開始。 通過ulimit修改隻是臨時,生效,要永久生效,可以寫到檔案中:

vim /etc/security/limits.conf
 * soft noproc 20000  #軟連接配接   
 * hard noproc 20000  #硬連接配接  
 * soft nofile 4096    
 * hard nofile 4096  
           
  • 辨別任意使用者 soft 辨別軟限制,超過會告警;hard:硬限制,超過報錯 nofile 打開檔案描述符數量 nproc 打開程序數量。

重新開機後生效,如果不生效檢視下登入子產品是否引入限制如下:

[root@localhost ~]# cat /etc/pam.d/login|grep pam_limits.so
session required pam_limits.so
           

limits.conf 檔案實際是 Linux PAM(插入式認證子產品,Pluggable Authentication Modules)中 pam_limits.so 的配置檔案,而且隻針對于單個會話。

如果不做限制,一個指令可導緻必須重新開機機器:

:(){ :|:; }; :
           

你在設定ulimit -n 的時候,如果設定一個很大的值,會提示沒有權限,即使你是root使用者如下:

[root@localhost ~]# ulimit -n 3229825
-bash: ulimit: open files: cannot modify limit: Operation not permitted
           

原因是我們設定的值超過了單個程序能打開的最大檔案數量:

cat /proc/sys/fs/nr_open
[root@localhost ~]# cat  /proc/sys/fs/nr_open
1048576
[root@localhost ~]# ulimit -n 1048576
[root@localhost ~]# ulimit -n 1048577
-bash: ulimit: open files: cannot modify limit: Operation not permitted
           

當我們設定小于等于單程序可以打開的最大檔案數量後,就可以設定成功了,臨時更改單個程序可以打開最大檔案數量如下:

[root@localhost ~]# echo 3229826 > /proc/sys/fs/nr_open
[root@localhost ~]# cat  /proc/sys/fs/nr_open
3229826
[root@localhost ~]# ulimit -n 1048577
           

永久設定生效:

/etc/sysctl.conf 中添加或修 fs.nr_open值。
           

通過sysctl -p 生效,或者通過指令設定:

sysctl -w fs.nr_open=1000000
           

fs.nr_open限制單個程序打開最大檔案數量

/proc/sys/fs/nr_open
This file imposes a ceiling on the value to which the RLIMIT_NOFILE resource limit can be raised (see getrlimit(2)). This ceiling is enforced for both unprivileged and privileged process. The default value in this file is 1048576.
           

二 檔案句柄和檔案描述符

我們上面通過open函數傳回的就是檔案描述符,它是一個整數,每個程序都包含一個記錄項,每個記錄項内部都包含一個檔案描述符表,裡面包含檔案描述符fd和一個檔案指針,指向一個檔案表,同一個檔案打開多次會對應不同的檔案表,不同的檔案描述符,同上面的例子;多個檔案描述符也可以指向不同的或相同的檔案表, 這個檔案表稱為檔案句柄,如下圖:

linux打開最大檔案數

為什麼要區分這兩者那,因為我們通常用lsof檢視的是檔案描述符的數量,file-nr為檔案句柄數量,通過指令檢視:

cat /proc/sys/fs/file-nr
           

得到三個值,含義:

已配置設定檔案句柄的數目 已配置設定未使用檔案句柄的數目 檔案句柄的最大數目
Historically, the three values in file-nr denoted the number of allocated file
handles,  the number of  allocated but  unused file  handles, and  the maximum
number of file handles. Linux 2.6 always  reports 0 as the number of free file
handles -- this  is not an error,  it just means that the  number of allocated
file handles exactly matches the number of used file handles.
           

file-nr檔案裡面的第一個字段代表的是核心配置設定的struct file的個數,也就是檔案句柄個數,而不是檔案描述符。通過上面的圖關系我們可以看到,一個檔案描述符肯定要有一個檔案句柄,反過來,有一個檔案句柄了,可能有多個檔案描述符指向它,比如通過dup指派檔案描述符的情況或者父子程序情況,子程序會複制父程序的檔案描述符表。

為什麼要區分檔案描述符和檔案句柄,那是因為我們通過lsof檢視的時候,檢視到的是檔案描述符,再測試下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>


int main(int argc,char * argv[])
{

   int i = 0;
   int fd = open("/dev/zero",O_RDONLY);
 
   for (i = 0; i < 1000; i++ ){
      // dup隻是會增加檔案描述符的數量,
       // 可能少量增加檔案句柄數量或不增加看原來是否打開
       int fdup = dup(fd);
       if (fdup >0 ) {
            printf("dup file:%d ,des:%d ok\n",i,fdup);
        }    else {
            printf("dup file:%d error.\n",i);
        } 
   }
   pause();
}
           

我們寫了這樣一段代碼,編譯再另外一個終端運作,運作前面,通過下面的指令檢視打開的句柄數量,和檔案描述符的數量;運作後發現,lsof檢視的檔案描述符的數量增加了一千多,而檔案句柄的數量沒有增加,說明了,lsof檢視的是檔案描述符,通過測試我們也發現了ulimit -n限制的就是檔案描述符的數量。

linux打開最大檔案數

是不是檔案描述符的數量一定比檔案句柄多那,找了個例子測試下:

#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void testm()
{
   char * addr = NULL;
   int fd = open("/dev/zero",O_RDONLY);
   if (fd == -1 ){
       printf("open error.\n");
     }
   addr = mmap(NULL,4096,PROT_READ,MAP_PRIVATE,fd,0);
  if (addr == MAP_FAILED)
         printf("Map error");
else {
    //  munmap(addr,4096);
   }
  close(fd);
}

int main(int argc,char * argv[])
{
   int i = 0;
   for (; i<1000; i++)
       testm();
   pause();
}

           

運作發現,檔案描述符的數量變化很小,而檔案句柄的數量增加一千個左右,原因是檔案描述符我們通過close(fd)關閉了,而通過mmap映射的記憶體塊沒有關閉,而檔案句柄包含了映射的記憶體,沒有釋放,通過:

[testm@localhost ~]$ pmap -p 7226|grep "dev/zero" |wc -l
1000
           

核對程序打開的映射記憶體是對的。

linux打開最大檔案數

如果把注釋的代碼放開: munmap(addr,4096); ,這樣的話檔案句柄就不會怎麼增加的。

三 file-max

file-max即整個linux系統能打開的檔案總數,預設值是系統總記憶體(以KB為機關)/10, 檢視辦法:

cat /proc/sys/fs/file-max
766846
           

更改file-max的大,臨時生效

echo 786046 > /proc/sys/fs/file-max
或
sysctl -w fs.file-max=786046
           

永久生效:

echo fs.file-max=786046 >> /etc/sysctl.conf
sysctl -p
           

說明:

man 5 proc:
 /proc/sys/fs/file-max
This file defines a system-wide limit on the number of open files for all processes. System calls that fail when encountering this limit fail with the error ENFILE.
           

四 總結

檔案句柄和檔案描述符是兩個不同東西,檔案句柄對應:

1. open系統調用打開檔案(path_openat核心函數)
2. 打開一個目錄(dentry_open函數)
3. 共享記憶體attach (do_shmat函數)
4. socket套接字(sock_alloc_file函數)
5. 管道(create_pipe_files函數)
6. epoll/inotify/signalfd等功能用到的匿名inode檔案系統(anon_inode_getfile函數)
           

通過:

cat /proc/sys/fs/file-nr
           

檢視占用的句柄數量

檔案描述符對應的有:

1. 檔案為REG
2. 目錄 DIR。
3. CHR表示字元裝置
4. BLK辨別塊裝置。
5. unix, FIFO, Ipv6分表表示UNIX域套接字,FIFO隊列和IP套接字。
           
lsof -n|awk ‘{print $2}’|sort|uniq -c|sort -nr|more | grep [PID]
           

指令看看程序打開的檔案描述符。

六 參考

[https://blog.csdn.net/u013256816/article/details/60778709](https://blog.csdn.net/u013256816/article/details/60778709)
[https://www.sohu.com/a/242941944_262549](https://www.sohu.com/a/242941944_262549)

           

繼續閱讀