很多以讹传讹的半桶水文章,都教人修改/etc/security/limits.conf配置文件来放宽“打开的文件数量”限制,如果可以再多一滴水的话,还会加一句“重启后生效”。
其实,使用这个配置文件,和使用ulimit -n命令一样,属于很经典的错误。
设置或放宽“打开的文件数量“限制,其本质是调用了setrlimit()函数,设置了RLIMIT_NOFILE资源。在有特权的程序中调用这个函数,可以提高上限(放宽限制),而普通权限的程序只能自己勒死自己和新生的子进程。
而/etc/security/limits.conf这个配置文件是怎么生效的呢?其实用dpkg -S或rpm -qf查一下就很容易知道,这个文件是pam_limits.so的配置文件,而pam_limits.so是在/etc/pam.d/中被login和sshd等多个配置文件声明将要被调用的。
系统开机的时候,1号进程init“自然而然”是root身份运行,其下属的getty/login和sshd进程,也都是root身份。这些程序都可以随意调用setrlimit。当身份认证(部分工作由PAM来做,所以可以读shadow文件)完成之后,login和sshd的子进程会为用户准备好session(网络登录调用pam_mkhomdir建设HOME目录、pam_limits模块设置rlimit、pam_env模块读取/etc/environment设置环境变量,甚至显示motd这种功能也是PAM模块实现的)并将自己降级到登录的用户身份,再启动一个shell给用户使用。
/etc/security/limits.conf 只对“调用过pam_limits.so“的登录过程有效。但并不是所有场景都经过这个过程的。而ulimit命令呢,它本身只是shell是一个内部命令而已,只对“该shell进程”及随后新产生的子进程有效。
但是需要放宽rlimit的程序,往往不是在shell中由用户手工运行的程序,而是提供大规模网络服务的后台进程。它们所需的rlimit,要在init脚本、service unit文件中设置;支持从root身份启动的服务,一般都有自行设置rlimit的能力。
如果不理解上面的内容,就容易引发一些莫名其妙的故障。比如之前我在FreeWheel工作的时候,前辈为后台服务写的的init脚本里没有调用ulimit -n命令,而在root用户的~/.bash_profile里有这个命令。造成的后果,就是开机自动启动该服务的时候,启动的是一个打开文件数量受限,以至于无法保持很多socket的网络服务,而当运维人员登录进去手工重启服务之后,又莫名其妙变好了,以至于没法检查这个故障到底是怎么发生的。