在 Linux 系統(tǒng)中匈勋,在每個進(jìn)程中都有一組資源限制,進(jìn)程默認(rèn)打開的最大文件數(shù)個數(shù)為 1024 個衬吆,可以通過如下配置查看:
#ulimit-n
1024
在應(yīng)用程序開發(fā)過程中俱恶,比如向 server 發(fā)起連接的客戶端超過 1024 個時比搭,server 由于 1024 個最大的文件個數(shù)限制而出現(xiàn)打開文件失敗,進(jìn)而出現(xiàn)Too many open files 錯誤爹凹。
在linux中這些限制是分為軟限制 (soft limit) 和 硬限制 ( hard limit )厨诸。他們的區(qū)別就是軟限制可以在程序的進(jìn)程中自行改變(突破限制),而硬限制則不行(除非有 root 權(quán)限)禾酱。
軟限制是內(nèi)核實(shí)際執(zhí)行的限制微酬,任何進(jìn)程都可以將軟限制設(shè)置為任意小于等于對進(jìn)程限制的硬限制的值。
硬限制充當(dāng)軟限制的上限:非特權(quán)進(jìn)程只能將其軟限制設(shè)置為從 0 到硬限制的范圍內(nèi)的值颤陶,只有超級用戶進(jìn)程可以提高硬限制的值颗管。
任何一個進(jìn)程都可以降低其硬限制的值,但它必須大于或等于其軟限制的值滓走。
查看軟限制的命令
#ulimit-Sn
1024
查看硬限制的命令
#ulimit-Hn
4096
那么如何設(shè)置這些限制呢垦江?
1、可以通過 getrlimit 和 setrlimit 系統(tǒng)調(diào)用的方式進(jìn)行獲取和設(shè)置資源限制搅方。
#include<sys/resource.h>
intgetrlimit(intresource,?struct?rlimit?*rlim);
intsetrlimit(intresource,conststruct?rlimit?*rlim);
對于這 2 個函數(shù)的每一次調(diào)用都會指定一個資源以及一個指向下列結(jié)構(gòu)的指針比吭。
structrlimit{
rlim_trlim_cur;/*?Soft?limit?*/
rlim_trlim_max;/*?Hard?limit?(ceiling?for?rlim_cur)?*/
};
比如在應(yīng)用程序啟動過程中绽族,通過調(diào)用 setrlimit 的方法進(jìn)行設(shè)置。
structrlimitrlim;
rlim.rlim_cur?=5000;
rlim.rlim_cur?=5000;
if(setrlimit(RLIMIT_NOFILE,?&rlim)?!=0)
{
printf("failed?to?set?rlimit?for?open?files\n");
return0;
}
2梗逮、可以在啟動服務(wù)腳本中進(jìn)行設(shè)置
#!/bin/bash
ulimit-c?unlimited
ulimit-n?5000
./exec&
對于通過命令行修改的 ulimit 配置是臨時生效的项秉。
3、也可以修改配置文件(永久生效)慷彤,如下
#?vi?/etc/security/limits.conf
...
//在文件末尾添加如下2行
*????soft??????nofile????5000
*????hard??????nofile????5000
配置完后娄蔼,重啟系統(tǒng),使之生效底哗。
4岁诉、當(dāng)服務(wù)已經(jīng)啟動時,這個時候修改 unlimt 是無效的跋选,可以通過如下命令進(jìn)行動態(tài)調(diào)整
//?centos?7?環(huán)境
1涕癣、通過ps命令獲取服務(wù)的pid
2、先查看其配置信息
#?cat?/proc/{pid}/limits
...
Max?open?files50005000files
...
3前标、修改配置
//centos?7 下
#?prlimit??--nofile=6000:6000??--pid?{pid}
//centos?6 下
#?echo?-n"Max?open?files=6000:6000">?/proc/{pid}/limits
setrlimit? 原理分析
接下里通過源碼的方式進(jìn)行分析下當(dāng)使用 setrlimit ?系統(tǒng)調(diào)用設(shè)置最大打開文件個數(shù)時發(fā)生了什么坠韩。
asmlinkagelongsys_setrlimit(unsignedintresource,structrlimit?__user?*rlim)
{
structrlimit?new_rlim,?*old_rlim;
unsignedlongit_prof_secs;
intretval;
if(resource?>=?RLIM_NLIMITS)
return-EINVAL;
if(copy_from_user(&new_rlim,?rlim,sizeof(*rlim)))
return-EFAULT;
if(new_rlim.rlim_cur?>?new_rlim.rlim_max)
return-EINVAL;
//獲取舊的資源配置
old_rlim?=?current->signal->rlim?+?resource;
if((new_rlim.rlim_max?>?old_rlim->rlim_max)?&&
!capable(CAP_SYS_RESOURCE))
return-EPERM;
if(resource?==?RLIMIT_NOFILE?&&?new_rlim.rlim_max?>?NR_OPEN)
return-EPERM;
...
task_lock(current->group_leader);
//把新的配置設(shè)置上去
*old_rlim?=?new_rlim;
task_unlock(current->group_leader);
if(resource?!=?RLIMIT_CPU)
gotoout;
...
out:
return0;
}
該方法就是把新的配置信息替換掉當(dāng)前進(jìn)程舊的資源限制信息。
當(dāng)系統(tǒng)打開一個文件時炼列,需要獲取一個未使用的文件描述符只搁,其過程如下
int?get_unused_fd_flags(int?flags)
{
struct?files_struct?*?files?=?current->files;
int?fd,?error;
struct?fdtable?*fdt;
error?=?-EMFILE;
spin_lock(&files->file_lock);
repeat:
fdt?=?files_fdtable(files);
//查找一個未使用的文件描述符
fd?=?find_next_zero_bit(fdt->open_fds->fds_bits,?fdt->max_fds,
files->next_fd);
/*
*?N.B.?For?clone?tasks?sharing?a?files?structure,?this?test
*?will?limit?the?total?number?of?files?that?can?be?opened.
*/
//?查看描述符是否超過資源限制,返回錯誤
if(fd?>=?current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
gotoout;
/*?Do?we?need?to?expand?the?fd?array?or?fd?set???*/
//根據(jù)fd進(jìn)行判斷是否需要擴(kuò)展文件描述表
error?=?expand_files(files,?fd);
if(error?<0)
gotoout;
if(error)?{
/*
*?If?we?needed?to?expand?the?fs?array?we
*?might?have?blocked?-?try?again.
*/
error?=?-EMFILE;
gotorepeat;
}
//設(shè)置fd的使用標(biāo)志
FD_SET(fd,?fdt->open_fds);
...
error?=?fd;
out:
spin_unlock(&files->file_lock);
returnerror;
}
從上述代碼可知俭尖,每當(dāng)打開一個文件時氢惋,都會判斷打開的文件描述符是否超過資源限制,若超過資源限制稽犁,則返回失敗焰望,錯誤碼為 Too many open files。
關(guān)注微信公眾號?Linux碼農(nóng) 獲取更多干貨
原文鏈接
推薦閱讀: