Linux 线程(进程)数限制分析
1.问题来源
公司线上环境出现MQ不能接受消息的异常,运维和开发人员临时切换另一台服务器的MQ后恢复。同时运维人员反馈在出现问题的服务器上很多基本的命令都不能运行,出现如下错误:
2. 初步原因分析和解决
让运维的兄弟在服务上查看内存、CPU、网络、IO等基本信息都正常。于是自己到运维的服务器上看了一下,下面是slabtop –s c的运行结果,问题初步原因貌似出现了:
如果看到这个截图你看不出什么异常的话,下面的内容你可能不感兴趣,哈哈。。。
task_struct是内核对进程的管理单位,通过slub(slab的升级版,如果你对slub不了解也不影响下面的内容,只要了解slab就行了)进行节点的管理,正常负载的服务不应该出现task_struct的slub结构体占用内存最大的情况,这说明这台服务器上开启了大量的进程(Linux内核态对进程和线程都是一个单位,不要纠结这个,后面可能会进程、线程混用)。
通过这个信息,兄弟们发现这台服务器上有近3万个线程,同时也定位到出问题的网元(一个新同学的代码没有Review直接上线,里面有一个BUG触发了异常创建大量线程)。
问题貌似到这里就结束了,但是作为一个有情怀的程序员,这只是一个开始(哥的情怀白天都被繁琐的工作磨没了,只能在这深夜独享了。。。)
3. Linux线程数的限制
3.1 应用层测试代码
#define MEMSIZE (1024 * 1024 * 256)
void thread(void)
{
sleep(100);
return;
}
int main()
{
pthread_t id;
int ret;
int num = 0;
while (1) {
ret = pthread_create(&id, NULL, (void*)thread, NULL);
++num;
if (ret != 0)
break;
}
printf("pthread_create fail with ret=%d, total num=%d\n", ret, num);
sleep(100);
return 0;
}
通过strace跟踪,发现问题出现在copy_process函数,那剩下的工作就是分析copy_process返回异常的原因了。
3.2 逆向分析
这个时候逆向分析最简单直接,可以直接定位到问题原因。
首先通过strace分析,查找出问题的系统调用是clone函数。
SYS_clone—>do_fork—>copy_process。内核态函数的分析工具这次试用了systemtap,下面就是没有任何美感的stap代码了,将就着看看吧
probe kernel.statement("*@kernel/fork.c:1184")
{
printf("In kernel/fork.c 1184\n");
}
probe kernel.statement("*@kernel/fork.c:1197")
{
printf("In kernel/fork.c 1197\n");
}
probe kernel.statement("*@kernel/fork.c:1206")
{
printf("1113.www.qixoo.qixoo.com In kernel/fork.c 1206\n");
}
probe kernel.statement("*@kernel/fork.c:1338")
{
printf("In kernel/fork.c 1338\n");
}
probe kernel.statement("*@kernel/fork.c:1342")
{
printf("In kernel/fork.c 1342\n");
}
probe kernel.statement("*@kernel/fork.c:1363")
{
printf("In kernel/fork.c 1363\n");
}
probe kernel.statement("*@kernel/fork.c:1369")
{
printf("In kernel/fork.c 1369\n");
}
probe kernel.statement("*@kernel/fork.c:1373")
{
printf("In kernel/fork.c 1373\n");
}
probe kernel.function("copy_process").return
{
printf("copy_process return %d\n", $return)
}
function check_null_pid:long(addr:long)
{
struct pid *p;
p = (struct pid*)THIS->l_addr;
if (p == NULL)
THIS->__retvalue = 0;
else
THIS->__retvalue = 1;
}
probe kernel.function("alloc_pid")
{
printf("alloc_pid init\n");
}
probe kernel.statement("*@kernel/pid.c:301")
{
printf("alloc_pid 301\n");
}
probe kernel.statement("*@kernel/pid.c:312")
{
printf("alloc_pid 312\n");
}
probe kernel.function("alloc_pid").return
{
printf("alloc_pid return %ld\n", check_null_pid($return));
}
发现问题出在alloc_pid失败,分析内核代码,这个受限于kernel.pid_max参数。
将参数调大到100000后,再次运行。
继续通过strace跟踪,这次发现问题出在了mprotect函数
这个问题是由于当个线程的mmap个数限制,受限于vm.max_map_count参数。
将参数调大到100000后,再次运行,线程数明显增加了。
其实这里面还有一个参数kernel.threads-max限制,由于系统默认将这个参数设置为800000,非常大,所以这个参数的影响一直没有保留出来。
后面又犯贱把相关的参数都设置成800000,结果内存耗尽,系统直接没响应了。。。。
3.3 正向分析
直接分析copy_process代码
copy_process
3.3.1 内存限制
dup_task_struct–>alloc_task_struct_node/alloc_thread_info_node/arch_dup_task_struct–>kmme_cache_alloc_node(slub.c)–>slab_alloc_node–>
“CONFIG_MEMCG_KMEM” //这里也是一个坑,docker这种基于cgroup的也会影响,可能会因为分配给slub的内存不够用出现线程限制
具体函数:
alloc_pages—->__memcg_kmem_newpage_charge–>memcg_charge_kmem–>__res_counter_charge–>res_counter_charge_locked
3.3.2 Threads-max 参数限制
if (nr_threads >= max_threads) // threads-max 参数影响
3.3.3 Pid_max 参数限制
alloc_pid–>alloc_pidmap //pid_max参数影响
3.3.4 单进程内存限制
单个进程的线程数,受限于vm.max_map_count限制
4. 总结
/proc/sys/kernel/pid_max #操作系统线程数限制
/proc/sys/kernel/thread-max #操作系统线程数
max_user_process(ulimit -u) #系统限制某用户下最多可以运行多少进程或线程
/proc/sys/vm/max_map_count #单进程mmap的限制会影响当个进程可创建的线程数
/sys/fs/cgroup/memory/${cgroup}/memory.kmem #单个docker 内核内存的限制,可以影响task_struct等slab节点的申请,间接影响可创建的线程数
Linux 线程(进程)数限制分析的更多相关文章
- (转)Linux 最大进程数
Linux 最大进程数 原文:https://www.cnblogs.com/pangguoping/p/5792075.html 前言 使用环境:centos 7系统 一.查看用户打开的最大进程数 ...
- Linux记录-进程数和句柄数调整
1.cat /etc/security/limits.confwebuser soft nofile 65535webuser hard nofile 65535webuser soft nproc ...
- linux最大进程数、最大打开文件数
ulimit 是一种 linux 系统的内键功能,它具有一套参数集,用于为由它生成的 shell 进程及其子进程的资源使用设置限制.本文将在后面的章节中详细说明 ulimit 的功能,使用以及它的影响 ...
- Linux 最大进程数
前言 使用环境:centos 7系统 一.查看用户打开的最大进程数 ulimit -a max user processes (-u) #系统限制某用户下最多可以运行多少进程 ...
- linux最大进程数
使用 ulimit -a 命令,查看 max user processes 的输出,就是系统最大进程数 core file size (blocks, -c) unlimited data seg s ...
- linux打开进程数测试
查看linux默认打开最大打开进程数 具体参考:https://www.jb51.net/article/143667.htm #include <unistd.h> #include & ...
- linux查看进程数
命令行: $ ps -ef | wc -l 如果想匹配某个关键词的话,加上grep,下面命令是匹配关键词 “XXX”,并统计含有该关键词的进程数 $ ps -ef | grep XXX | wc -l
- LINUX最大线程数及最大进程数
查看最大线程数: cat /proc/sys/kernel/threads-max ulimit User limits - limit the use of system-wide resource ...
- linux查看进程的线程数
top -H -p $PID #查看对应进程的那个线程占用CPU过高 1.top -H 手册中说:-H : Threads toggle 加上这个选项启动top,top一行显示一个线程.否则,它一行 ...
随机推荐
- 024医疗项目-模块二:药品目录的导入导出-HSSF导入类的学习
我们之前学习了怎么把数据的数据导出来保存到Excle中,这篇文章我们学习怎么Excel数据导出然后插入到数据库中. 我们先学习HSSF怎么用来导出数据. 看官方教程步骤如下: 第一步: 创建一个wor ...
- 整理MAC下Eclipse的常用快捷键
整理Eclipse常用快捷键 开发环境切换到Mac下后原来Window下的快捷键很大一部分是不相容的,习惯了快捷键的生活忽然哪天快捷键不起作用了,跟着的就是开发效率明显降低,频繁录入错误的快捷键让Ec ...
- connect函数详解
不得不说,客户端的connect函数和服务端的accept函数是一对好基友,如果客户端没有去connect, 那么服务端的accept会一直在那里傻傻地痴痴地等待,我们先来看看connect函数的原型 ...
- 妙味WEB前端开发全套视频教程+项目实战+移动端开发(99G)
一共99GB的视频教程,全部存于百度网盘中,13个栏目,每个栏目里还划分有独立的小栏目 最基本的web前端学习介绍,到项目实战,再到移动端的开发,真正彻底掌握前端开发的精髓: 视频教程在线预览:(百度 ...
- MATLAB中提高fwrite和fprintf函数的I/O性能
提高fwrite和fprintf函数的I/O性能 http://www.matlabsky.com/thread-34861-1-1.html 今天我们将讨论下著名的fwrite(fprint ...
- java.util.ConcurrentModificationException 解决办法
在使用iterator.hasNext()操作迭代器的时候,如果此时迭代的对象发生改变,比如插入了新数据,或者有数据被删除. 则使用会报以下异常:Java.util.ConcurrentModific ...
- RF源码阅读(碎片纪录)-Python积木之contextlib
参考页面: http://docs.python.org/2/library/contextlib.html contextlib是为了配合with语句来使用的.使用起来更加简洁.本来想写一下,这位同 ...
- IT男的”幸福”生活"续4
翻来翻去,总是睡不觉.大脑口一堆问题.一个又冒出一个,没完没了.明天该怎样去进行下一步呢.. ….. 夜一下子深黑很多,窗外的公路,时而有货车通过,动不动按喇叭,而我住在二楼,真它的吵.也许她住在五楼 ...
- 最简单的 Web Service 入门 (看了包会)
原理:WebService是一个SOA(面向服务的编程)的架构,它是不依赖于语言,不依赖于平台,可以实现不同的语言间的相互调用,通过Internet进行基于SOAP协议的网络应用间的交互. 作用:主要 ...
- 数据结构之链表、栈和队列 java代码实现
定义抽象节点类Node: package cn.wzbrilliant.datastructure; /** * 节点 * @author ice * */ public abstract class ...