平台人生
作者平台人生·2017-08-31 12:04
软件开发工程师·平台人生

说说Linux操作系统中nproc的那些事

字数 4169阅读 6598评论 0赞 5

作者:刘庆


众所周知,Linux操作系统中利用ulimit限制shell启动进程占用的资源,ulimit支持对用户的打开进程数、进程打开文件句柄数、进程打开文件的大小、进程coredump文件大小等资源进行限制,从而防止某个用户进程过度占用系统资源,避免影响整个操作系统和其他应用的正常运行。但是使用ulimit进行限制后,当用户资源超限制时会出现各种各样的报错,最近小编经常遇到用户打开进程数超过限制后带来的各种问题,特来跟大家谈一谈用户打开进程数,即nproc参数。

首先咱们来看看几个故障案例:

故障案例

故障一:同学A前来报障:“赶紧帮我看看,我的应用用户今天的crontab任务没有执行!”
小编登录主机,看了一下/var/log/cron日志,里面有报错信息:

Aug 2 10:09:01 prod-machine crond[13234]: (CRON) ERROR (setreuid failed): Resource temporarily unavailable
Aug 2 10:09:01 prod-machine crond[13234]: (produser) ERROR (failed to change user)

故障二:同学B前来报障:“赶紧帮我看看,我的应用用户登录不了系统了!”
小编登录主机,看了一下/var/log/secure日志,里面有报错信息:

Aug 2 11:21:41 prod-machine sshd[15343]: fatal: setresuid 901: Resource temporarily unavailable
Aug 2 11:21:41 prod-machine sshd[15336]: pam_unix(sshd:session): session closed for user produser

故障原因

在这两个故障案例中,2份不同主机日志里均含有共同的报错信息:Resource temporarily unavailable,中文理解就是资源暂时不可用,其原因大概有3种:

  1. 用户的nproc达到限制,无法创建新的进程
  2. 系统没有可分配的的pid,即进程号已经达到内核参数kernel.pid_max的限制
  3. 系统可用内存低,新的进程无法申请到内存导致不能启动。

在小编遇到的生产故障中,基本上是第一种原因。现在咱们就来探讨一下用户nproc达到限制时,如何排查。

故障排查

由于是用户打开进程数达到了限制,那么排查方法主要是查看用户目前打开的进程的数量,以上面出现问题的produser用户为例:

(1)

ps -u produser –f

此方法是查看当前produser用户打开的进程数量。

但是,(1)方法在用户应用是多线程模式下不好使,使用此统计方法,统计出来的进程数量要小于限制值。那么应该如何查询,我们先来看一段官方文档 man 2 setrlimit:

RLIMIT_NPROC
The maximum number of processes (or, more precisely on Linux, threads) that can be created for the real user ID of the calling process. Upon encountering this limit, fork(2) fails with the error EAGAIN.

因此在Linux主机上我们应该统计用户打开的进程线程的数量,ps命令需要加-L参数用于查看线程,所以给出下面的命令:

(2)

ps -u produser –Lf 

大部分情况使(2)方法就能查看的结果。但是小编需要说的是⑵方法仍然存在问题,统计出来的进程数在某些情况下仍然小于限制值,再看一下上面的文档,其实nproc限制值是限制的real user创建的进程线程数。进程创建的时候存在real user和effective user两个属性,用ps命令统计的时候默认显示的是effective user的进程数,当进程的real user跟effective user不一致的时候会导致上面的命令统计的结果小于限制值。 上面-u参数查看的是effective user,因此这里再改进一下,使用-U参数查real user为produser的用户:

(3)

ps -U produser –Lf 

这个命令也是根据实际案例发现的:我们生产上存在部分用户的crontab任务没有加标准输出和错误输出的重定向,同时系统的postfix服务没有打开,这样crontab任务输出会以邮件形式存放到/var/spool/postfix/maildrop目录,最终导致文件系统的inode使用率过高。一日我们工程师在对maildrop清理的时候不小心删除了maildrop目录,crond进程的子进程会hang住,结果用户进程数超过限制,导致其他用户应用出现问题、无法登录等问题。最开始使用的是⑵方法统计的线程数量,结果小于用户的限制值,后来发现crond进程在执行用户任务的时候会开辟一个CROND的子进程切换到对应用户执行相应的任务,其中这些CROND的子进程对应的effective user为root,real user为produser,如下所示:

root@prod-machine:~>ps -C crond -o euser,ruser,pid,ppid,cmd
EUSER RUSER PID PPID CMD
root root 1946 1 crond
root produser 28108 1946 CROND
root produser 28140 1946 CROND
root produser 28172 1946 CROND
root produser 28204 1946 CROND

因此当我们要统计用户打开的进程线程数的任务,一定要统计real user的进程线程数,下面的方法可以正确统计主机所有用户的进程线程数:

ps -eLo ruser | awk 'NR>1' | sort | uniq –c

上面主要给大家介绍了用户nproc达到限制值时分析的方法。接着给大家介绍一个案例,主要谈谈用户nproc hard的默认值是怎么取到的。

小编一天制作了一台1C2G的RHEL6版本虚拟机,用于制作虚拟机模板,小编在/etc/security/limits.d/90-nproc.conf修改了用户nproc的soft值,如下:

* soft nproc 10240
root soft nproc unlimited

该配置设置了root用户的nproc的soft值为ulimited,其他用户的nproc的soft值为10240。然而当检查每个用户的nproc值的时候惊奇地发现用户的nproc的hard和soft值均为7387,如下:

produser@prod-machine:~>ulimit -Su
7387
produser@prod-machine:~>ulimit -Hu
7387

这个值很奇怪,用户nproc的soft和hard值并不是10240,但是在其他主机上这个配置能够达到预期的值。经过分析,小编认为上面的配置中由于并没有设置用户nproc的hard值,导致用户nproc的soft值受到hard值的限制,取不到预期的值,因此关键问题需要搞清楚hard值是由哪个因素决定的。小编琢磨了一下,认为nproc的hard值是在内核获取到的,所以翻看了内核代码,果然在内核代码fork.c中发现端倪:

void __init fork_init(unsigned long   mempages)
{
    ...
        max_threads = mempages / (8 * THREAD_SIZE / PAGE_SIZE);
 
        /*
         * we need to allow at least 20   threads to boot a system
         */
        if(max_threads < 20)
                max_threads = 20;
 
          init_task.signal->rlim[RLIMIT_NPROC].rlim_cur = max_threads/2;
          init_task.signal->rlim[RLIMIT_NPROC].rlim_max = max_threads/2;
    ...
}

其中mempages是物理内存页的个数,PAGE_SIZE为4K,THREAD_SIZE在小编的x86平台是16K,所以最后默认的nproc值为:

default_nproc = max_threads/2
= MemTotal(KB) / 256(KB)

即默认nproc大小取决于主机内存大小。在小编2G内存(实际虚拟机内存为1877M,Hypervisor管理虚拟机有内存开销)的虚拟机上,计算一下:

default_nproc = 1877*1024/256 = 7508

由于kernel会占用一部分的内存,实际的nproc(本例中为7387)要比计算值稍小一些。小编的案例中仅仅设置了用户nproc的soft值,所以用户的nproc的soft值被限制在default_nproc。因此上面例子中有两种解决方法:

  1. 评估需求,用户nproc的要是少于7387的话,就降低;
  2. 在配置文件中,设置上用户nproc的hard值为10240,通过提升hard值来满足预期。

因此各位看官今后要根据主机内存配置合理地设置用户nproc的值。

如果觉得我的文章对您有用,请点赞。您的支持将鼓励我继续创作!

5

添加新评论0 条评论

Ctrl+Enter 发表

本文隶属于专栏

作者其他文章

相关文章

相关问题

相关资料

X社区推广