自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

刨根問底兒,看我如何處理 Too Many Open Files 錯誤!

開發(fā) 項目管理
如果你的項目中支持高并發(fā),或者是測試過比較多的并發(fā)連接。那么相信你一定遇到過“Too many open files”這個錯誤。

[[381472]]

 本文轉(zhuǎn)載自微信公眾號「開發(fā)內(nèi)功修煉」,作者張彥飛allen 。轉(zhuǎn)載本文請聯(lián)系開發(fā)內(nèi)功修煉公眾號。   

如果你的項目中支持高并發(fā),或者是測試過比較多的并發(fā)連接。那么相信你一定遇到過“Too many open files”這個錯誤。

這個錯誤的出現(xiàn)其實是正常的,因為每打開一個文件(包括socket),都需要消耗一定的內(nèi)存資源。為了避免個別進程不受控制地打開了過多的文件而讓整個服務(wù)器崩潰,Linux 對打開的文件描述符數(shù)量有限制。

但是解決這個錯誤“奇葩”的地方在于,竟然需要修改三個參數(shù):fs.nr_open、nofile(其實 nofile 還分 soft 和 hard) 和 fs.file-max。這幾個參數(shù)里有的是進程級的、有的是系統(tǒng)級的、有的是用戶進程級的,說一遍都覺得好亂。而且另外這幾個參數(shù)還有依賴關(guān)系,著實比較復雜。

不知道你,反正飛哥我是根本記不住哪個是哪個。每次遇到這種問題,還是都得再繼續(xù) Google 一遍。但由于復雜性,所以其實網(wǎng)上的很多帖子里也都并沒有真正搞清楚。如果照搜索出來的文章改,稍有不慎就會踩雷,把機器搞出問題。

我在測試最大TCP連接數(shù)的時候就踩過兩次坑。

第一次是當時開了二十個子進程,每個子進程開啟了五萬個并發(fā)連接興高采烈準備測試百萬并發(fā)。結(jié)果倒霉催地忘了改 file-max 了。實驗剛開始沒多大一會兒就開始報錯“Too many open files”。但問題是這個時候更悲催的是發(fā)現(xiàn)所有的命令包括 ps、kill也同時無法使用了。因為它們也都需要打開文件才能工作。后來沒辦法,重啟系統(tǒng)解決的。

另外一次是重啟機器完了之后發(fā)現(xiàn)無法 ssh 登錄了。后來找運維工程部的同學報障以后才算是修復。最終發(fā)現(xiàn)是因為 hard nofile 比 fs.nr_open 高了,直接導致無法登陸。(其實我把 fs.nr_open 加大過,但是用的是 echo 命令 修改的。系統(tǒng)一重啟,還原了)。

一、找到源代碼

對于這三個家伙,我真的是無法言語更多了。所以我下定了決心,一定要把它們徹底搞清楚。怎么搞?那沒有比把它的源碼扒出來能看的更準確了。我們就拿創(chuàng)建 socket 來舉例,首先找到 socket 系統(tǒng)調(diào)用的入口

  1. //file: net/socket.c 
  2. SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol) 
  3.  retval = sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK)); 
  4.  if (retval < 0) 
  5.   goto out_release; 

我們看到 socket 調(diào)用 sock_map_fd 來創(chuàng)建相關(guān)內(nèi)核對象。接著我們再進入 sock_map_fd 瞧瞧。

  1. //file: net/socket.c 
  2. static int sock_map_fd(struct socket *sock, int flags) 
  3.  struct file *newfile; 
  4.  
  5.  //在這里會判斷打開文件數(shù)是否超過 soft nofile 和 fs.nr_open 
  6.  //獲取 fd 句柄號 
  7.  int fd = get_unused_fd_flags(flags);   
  8.  if (unlikely(fd < 0)) 
  9.   return fd; 
  10.  
  11.  //在這里會判斷打開文件數(shù)是否超過 fs.file-max 
  12.  //創(chuàng)建 sock_alloc_file對象 
  13.  newfile = sock_alloc_file(sock, flags, NULL);  
  14.  if (likely(!IS_ERR(newfile))) { 
  15.   fd_install(fd, newfile); 
  16.   return fd; 
  17.  } 
  18.  
  19.  put_unused_fd(fd); 
  20.  return PTR_ERR(newfile); 

為什么創(chuàng)建一個socket又要申請 fd,又要申請 sock_alloc_file 呢?我們看一個進程打開文件時的內(nèi)核數(shù)據(jù)結(jié)構(gòu)圖就明白了

結(jié)合上圖,就能輕松理解這兩個函數(shù)的作用

  • get_unused_fd_flags:申請 fd,這只是一個在找一個可用的數(shù)組下標而已
  • sock_alloc_file:申請真正的 file 內(nèi)核對象

二、找到進程級限制 nofile 和 fs.nr_open

接下來,我們再回到最大文件數(shù)量的判斷上。這里我直接把結(jié)論拋出來。get_unused_fd_flags 中判斷了 nofile、和 fs.nr_open。如果超過了這兩個參數(shù),就會報錯。請看!

  1. //file: fs/file.c 
  2. int get_unused_fd_flags(unsigned flags) 
  3.  // RLIMIT_NOFILE 是 limits.conf 中配置的 nofile 
  4.  return __alloc_fd( 
  5.   current->files,  
  6.   0,  
  7.   rlimit(RLIMIT_NOFILE),  
  8.   flags 
  9.  ); 

在get_unused_fd_flags 中,調(diào)用了 rlimit(RLIMIT_NOFILE)。這個是讀取的 limits.conf 中配置的 soft nofile,代碼如下:

  1. //file: include/linux/sched.h 
  2. static inline unsigned long task_rlimit(const struct task_struct *tsk, 
  3.   unsigned int limit) 
  4.  return ACCESS_ONCE(tsk->signal->rlim[limit].rlim_cur); 

通過當前進程描述訪問到 rlim[RLIMIT_NOFILE],這個對象的 rlim_cur 是 soft nofile(rlim_max 對應(yīng) hard nofile )。

緊接著讓我們進入 __alloc_fd() 中來

  1. //file: include/uapi/asm-generic/errno-base.h 
  2. #define EMFILE  24 /* Too many open files */ 
  3.  
  4. int __alloc_fd(struct files_struct *files, 
  5.         unsigned start, unsigned end, unsigned flags) 
  6.  ... 
  7.  error = -EMFILE; 
  8.  
  9.  //看要分配的文件號是否超過 end(limits.conf 中的 nofile) 
  10.  if (fd >= end
  11.   goto out
  12.  
  13.  error = expand_files(files, fd); 
  14.  if (error < 0) 
  15.   goto out
  16.  ... 

在__alloc_fd() 中會判斷要分配的句柄號是不是超過了 limits.conf 中 nofile 的限制。fd 是當前進程相關(guān)的,是一個從 0 開始的整數(shù)。如果超限,就報錯 EMFILE (Too many open files)。

這里注意個小細節(jié),那就是進程里的 fd 是一個從 0 開始的整數(shù)。只要確保分配出去的 fd 編號不超過 limits.conf 中 nofile,就能保證該進程打開的文件總數(shù)不會超過這個數(shù)。

接著我們看到調(diào)用又會進入 expand_files:

  1. static int expand_files(struct files_struct *files, int nr) 
  2.  //2. 判斷打開文件數(shù)是否超過 fs.nr_open 
  3.  if (nr >= sysctl_nr_open)    
  4.   return -EMFILE; 

在 expand_files 我們看到,又到 nr (就是 fd 編號) 和 fs.nr_open 相比較了。超過這個限制,返回錯誤 EMFILE (Too many open files)。

由上可見,無論是和 fs.nr_open,還是和 soft nofile 比較,都用的是當前進程的文件描述符序號在比較的,所以這兩個參數(shù)都是進程級別的。

有意思的是和這兩個參數(shù)的比較幾乎是前后腳進行的,所以它兩的作用也基本一樣。Linux之所以分兩個參數(shù)來控制,那是因為 fs.nr_open 是系統(tǒng)全局的,而 nofile 則可以分用戶來分別控制。

所以,現(xiàn)在我們可以得出第一個結(jié)論。

結(jié)論1:soft nofile 和 fs.nr_open的作用一樣,它兩都是限制的單個進程的最大文件數(shù)量。區(qū)別是 soft nofile 可以按用戶來配置,而 fs.nr_open 所有用戶只能配一個。

三、找到系統(tǒng)級限制 fs.nr_open

我們在回過頭來看 sock_map_fd 中調(diào)用的另外一個函數(shù) sock_alloc_file,在這個函數(shù)里我們發(fā)現(xiàn)它會和 fs.file-max 這個系統(tǒng)參數(shù)來比較。用啥比的呢?

  1. //file: fs/file_table.c 
  2. struct file *sock_alloc_file(struct socket *sock, int flags, const char *dname) 
  3.  file = alloc_file(&path, FMODE_READ | FMODE_WRITE, 
  4.    &socket_file_ops); 
  5.  
  6. struct file *alloc_file(struct path *path, fmode_t mode, 
  7.   const struct file_operations *fop) 
  8.  file = get_empty_filp(); 
  9.  ... 
  10.  
  11. struct file *get_empty_filp(void) 
  12.  //files_stat.max_files就是 fs.file-max參數(shù) 
  13.  if (get_nr_files() >= files_stat.max_files  
  14.   && !capable(CAP_SYS_ADMIN) //注意這里root賬號并不受限制 
  15.   ) { 
  16.  } 

可見是用 get_nr_files() 來和 fs.file-max來比較的。根據(jù)該函數(shù)的注釋我們能看到它是當前系統(tǒng)打開的文件描述符總量。如下:

  1. /* 
  2.  * Return the total number of open files in the system 
  3.  */ 
  4. static long get_nr_files(void) 
  5.  ... 

另外注意下 !capable(CAP_SYS_ADMIN) 這行??赐赀@句,我才恍然大悟,原來 file-max 這個參數(shù)只限制非 root 用戶。開篇中我提到的文件打開過多時無法使用 ps,kill 等命令,是因為我用的非 root 賬號操作的。哎,下次再遇到這種文件直接用 root 去 kill 就行了。之前竟然丟臉地采用了重啟機器大法。。

所以現(xiàn)在我們可以得出另一個結(jié)論了。

結(jié)論2:fs.file-max: 整個系統(tǒng)上可打開的最大文件數(shù),但不限制 root 用戶

總結(jié)一下

我們總結(jié)一下,其實在 Linux 上能打開多少個文件,限制有兩種:

  • 第一種,進程級別的,限制的是單個進程上可打開的文件數(shù)。具體參數(shù)是 soft nofile 和 fs.nr_open。它們兩個的區(qū)別是 soft nofile 可以不同用戶配置不同的值。而 fs.nr_open 在一臺 Linux 上只能配一次。
  • 第二種,系統(tǒng)級別的,整個系統(tǒng)上可打開的最大文件數(shù),具體參數(shù)是fs.file-max。但是這個參數(shù)不限制 root 用戶。

另外這幾個參數(shù)之間還有耦合關(guān)系,因此還要注意以下三點:

  • 1、如果你想加大 soft nofile, 那么 hard nofile 也需要一起調(diào)整。因為如果 hard nofile 設(shè)置的低, 你的 soft nofile 設(shè)置的再高都沒用,實際生效的值會按二者里最低的來。
  • 2、如果你加大了 hard nofile,那么 fs.nr_open 也都需要跟著一起調(diào)整。如果不小心把 hard nofile 設(shè)置的比 fs.nr_open 大了,后果比較嚴重。會導致該用戶無法登陸。如果設(shè)置的是 * 的話,那么所有的用戶都無法登陸。
  • 3、還要注意如果你加大了 fs.nr_open,但是用的是 echo "xx" > ../fs/nr_open 的方式,剛改完你可能覺得沒問題。只要機器一重啟你的 fs.nr_open 設(shè)置就會失效,還是會無法登陸。

假如你想讓你的進程可以打開 100 萬個文件描述符,我覺得比較穩(wěn)妥點的修改方法是干脆都直接用 conf 文件的方式來改。這樣比較統(tǒng)一,也比較安全。

  1. # vi /etc/sysctl.conf 
  2. fs.nr_open=1100000  //要比 hard nofile 大一點 
  3. fs.file-max=1100000 //多留點buffer 
  4. # sysctl -p 
  5. # vi /etc/security/limits.conf 
  6. *  soft  nofile  1000000 
  7. *  hard  nofile  1000000 

通過這種方式修改,你就可以繞過飛哥踩過的坑了。

 

責任編輯:武曉燕 來源: 開發(fā)內(nèi)功修煉
相關(guān)推薦

2019-07-04 10:49:13

HTTPWebSocket協(xié)議

2015-07-02 15:04:53

CSS好奇心+

2017-01-09 16:35:25

socket函數(shù)fd備用

2022-04-20 11:41:45

Kafka數(shù)據(jù)解決方案

2023-04-26 00:06:22

服務(wù)器死循環(huán)報錯

2013-10-10 15:41:38

綠色數(shù)據(jù)中心數(shù)據(jù)中心

2012-09-07 09:23:01

Win 8操作系統(tǒng)

2010-03-22 16:51:31

無線網(wǎng)絡(luò)穩(wěn)定性

2023-02-07 08:36:32

2020-11-13 07:14:55

Kafka消息中間件

2024-07-07 21:39:34

2019-08-09 11:25:01

Java虛擬機Java程序員

2024-09-04 16:00:24

PostgreSQL數(shù)據(jù)庫

2019-06-18 15:20:01

MySQL連接錯誤數(shù)據(jù)庫

2022-12-06 09:10:56

KVC原理數(shù)據(jù)篩選

2011-02-22 13:38:45

VSFTPD

2020-04-09 13:38:40

MySQL數(shù)據(jù)庫臟讀

2019-08-15 10:20:19

云計算技術(shù)安全

2011-05-05 10:32:54

激光打印機

2012-12-12 09:49:41

點贊
收藏

51CTO技術(shù)棧公眾號