1. 前言
本文不一定适合比较老版本的Linux,如果只关心使用,请直接看“总结”,本文主要针对CentOS,其它Linux发行版本类似,但细节可能有出入,比如重启服务可能不是用systemctl,而是service等。
当需要调整一个进程可打开的最多文件数或SOCKET连接数等,以CentOS为例,通常的做法是修改文件/etc/security/limits.conf,比如将最多可打开数调整为10万:
# vi /etc/security/limits.conf
* soft nofile 100000
* hard nofile 100000
读取limit.conf文件的并不是Linux内核,而是一个内核模块PAM,对应的模块文件为:
/usr/lib64/security/pam_limits.so
/usr/lib/security/pam_limits.so
而/etc/pam.d目录下的配置文件,则由libpam.so读取,实际上所有的模块均由libpam.so加载,可将libpam.so看成是所有PAM模块的框架或容器,而且libpam.so本身也不是内核的组成部分。
多个不同Linux版本上查看,并没有叫libpam.so的文件名,均是libpam.so.0(不清楚是否所有都这样),但是编译Linux-PAM-1.3.1源代码有名为libpam.so软链接,指向libpam.so.0.84.2。
/usr/lib64/libpam.so.0 -> libpam.so.0.83.1
/usr/lib64/libpam.so.0.83.1
/usr/lib64/libpam_misc.so.0.82.0
/usr/lib/libpam.so.0 -> libpam.so.0.83.1
/usr/lib/libpam.so.0.83.1
/usr/lib/libpam_misc.so.0.82.0
libpam.so会被加载到crond等进程空间(那当然也可以不加载),如果没有加载libpam.so,则limits.conf不会生效。crond等不会主动加载libpam.so,那么是谁让libpam.so进入crond等进程空间的了?(执行“grep libpam /proc/`pidof crond`/maps”可查看libpam是否在crond的进程空间)。
在CentOS,可用service来启动或重启crond,所以跟它应当是相关的,而service实际调用的是systemctl这一系统工具(非Shell脚本,service为老版本使用方式,使用systemctl启动和重启服务,使用方式和service相同)。
# service crond restart
Redirecting to /bin/systemctl restart crond.service
# file /bin/systemctl
/bin/systemctl: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV)
# systemctl crond restart # 重启crontab服务进程crond
2. PAM
PAM的全称为“Pluggable Authentication Modules”,即可插入认证模块。最初由太阳微系统公司(Sun Microsystems,已于2009年被甲骨文收购)于1995年在Solaris开发。PAM代码不包含在Linux内核中,并有专门的网站:http://linux-pam.org/,源代码托管在Github上(https://github.com/linux-pam/linux-pam/releases)。
3. pam_limits
pam_limits是PAM其中的一个模块(模块文件名为pam_limits.so),也是程序员接触较多的模型之一,对应的源代码文件为pam_limits.c,代码规模为几百行,加上所有注释和空格有1100多行:
#if !defined(linux) && !defined(__linux)
#warning THIS CODE IS KNOWN TO WORK ONLY ON LINUX !!!
#endif
源代码提供autoconf编译,尝试在Linux-3.10上可编译成功:
~/Linux-PAM-1.3.1]$ ./configure --prefix=/usr/local/Linux-PAM-1.3.1
make
4. limits.conf的由来
确定模块pam_limits的配置文件,由宏CONF_FILE决定:
// pam_limits.c
#define CONF_FILE (pl->conf_file != NULL)?pl->conf_file:LIMITS_FILE
使用的地方:
// pam_limits.c
static int
parse_config_file(pam_handle_t *pamh, const char *uname, uid_t uid, gid_t gid, int ctrl, struct pam_limit_s *pl)
{
FILE *fil;
char buf[LINE_LENGTH];
/* check for the LIMITS_FILE */
if (ctrl & PAM_DEBUG_ARG)
pam_syslog(pamh, LOG_DEBUG, "reading settings from '%s'", CONF_FILE);
fil = fopen(CONF_FILE, "r"); // 打开配置文件,跟参数“pl”有关系
if (fil == NULL) {
pam_syslog (pamh, LOG_WARNING,
"cannot read settings from %s: %m", CONF_FILE);
return PAM_SERVICE_ERR;
}
如果函数parse_config_file的参数“pl”值为NULL,则配置文件名在编译时决定,这种情况下,配置文件名被固定为limits.conf:
# Makefile.am
modules/pam_limits/Makefile.am: -DLIMITS_FILE_DIR=\"$(limits_conf_dir)/*.conf\" \
modules/pam_limits/Makefile.am: -DLIMITS_FILE=\"$(SCONFIGDIR)/limits.conf\"
只是limits.conf所在目录可由编译时决定,也就是看SCONFIGDIR,决定在automake的configure.ac文件:
# co