原标题:故障案例 | 说说 Linux 操作系统中nproc的那些事
众所周知,Linux操作系统中利用ulimit限制shell启动进程占用的资源,ulimit支持对用户的打开进程数、进程打开文件句柄数、进程打开文件的大小、进程coredump文件大小等资源进行限制,从而防止某个用户进程过度占用系统资源,避免影响整个操作系统和其他应用的正常运行。但是使用ulimit进行限制后,当用户资源超限制时会出现各种各样的报错,最近笔者经常遇到用户打开进程数超过限制后带来的各种问题,特来跟大家谈一谈用户打开进程数,即nproc参数。
首先咱们来看看几个故障案例:
故障案例
故障一:同学A前来报障:“赶紧帮我看看,我的应用用户今天的crontab任务没有执行!”
笔者登录主机,看了一下/var/log/cron日志,里面有报错信息:
故障二:同学B前来报障:“赶紧帮我看看,我的应用用户登录不了系统了!”
笔者登录主机,看了一下/var/log/secure日志,里面有报错信息:
故障原因
在这两个故障案例中,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:
因此在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,如下所示:
因此当我们要统计用户打开的进程线程数的任务,一定要统计real user的进程线程数,下面的方法可以正确统计主机所有用户的进程线程数:
上面主要给大家介绍了用户nproc达到限制值时分析的方法。接着给大家介绍一个案例,主要谈谈用户nproc hard的默认值是怎么取到的。
笔者一天制作了一台1C2G的RHEL6版本虚拟机,用于制作虚拟机模板,笔者在/etc/security/limits.d/90-nproc.conf修改了用户nproc的soft值,如下:
该配置设置了root用户的nproc的soft值为ulimited,其他用户的nproc的soft值为10240。然而当检查每个用户的nproc值的时候惊奇地发现用户的nproc的hard和soft值均为7387,如下:
这个值很奇怪,用户nproc的soft和hard值并不是10240,但是在其他主机上这个配置能够达到预期的值。经过分析,笔者认为上面的配置中由于并没有设置用户nproc的hard值,导致用户nproc的soft值受到hard值的限制,取不到预期的值,因此关键问题需要搞清楚hard值是由哪个因素决定的。笔者琢磨了一下,认为nproc的hard值是在内核获取到的,所以翻看了内核代码,果然在内核代码fork.c中发现端倪:
其中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的值。
看更多文章可以到社区“平台人生”专栏
http://www.talkwithtrend.com/Column/detail/id/11返回搜狐,查看更多
责任编辑: