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节点的申请,间接影响可创建的线程数

时间: 2024-10-01 13:56:45

Linux 线程(进程)数限制分析的相关文章

Linux 最大进程数

前言 使用环境:centos 7系统 一.查看用户打开的最大进程数 ulimit -a max user processes              (-u) #系统限制某用户下最多可以运行多少进程或线程 二.这些个值是怎么来的? root 账号下 ulimit -u 出现的max user processes 的值默认是 # cat /proc/sys/kernel/threads-max的值/2,即系统线程数的一半 普通账号下 ulimit -u  出现的max user processe

解除linux最大进程数和最大文件打开数

说明: linux对于每个用户,系统限制其最大进程数.为提高性能,可以根据设备资源情况,可设置各      linux用户的最大进程数和每个进程可打开的文件数 1. 查看Linux的最大进程数和最大打开文件数 : ulimit -a 2. 修改Linux的最大进程数和最大文件打开数 打开vim /etc/security/limits.conf 添加如下内容即可 *       soft    nproc   65535 *       hard    nproc   65535 *      

linux打开进程数测试

查看linux默认打开最大打开进程数 具体参考:https://www.jb51.net/article/143667.htm #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #define MAXPROCESS 65535 #define SLEEPTIME 60 int main(int argc, char **argv) { pid_t pi

linux最大进程数

使用 ulimit -a 命令,查看 max user processes 的输出,就是系统最大进程数 core file size (blocks, -c) unlimited data seg size (kbytes, -d) unlimited scheduling priority (-e) 0 file size (blocks, -f) unlimited pending signals (-i) 31216 max locked memory (kbytes, -l) 16384

第一次作业:关于Linux进程模型的分析

1.前言 本文主要基于Linux Kernel 2.6.32 的源码,对Linux的进程模型进行分析,大致可以概括为如下内容: 1.前言 2.进程介绍 3.操作系统如何组织进程 4.进程状态的转化 5.进程的调度 6.参考资料 2.进程介绍 2.1 进程的概念 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础.在早期面向进程设计的计算机结构中,进程是程序的基本执行实体:在当代面向线程设计的计算机结构中,进程是线程的容器

线程 进程 多线程 多进程

进程和线程的主要区别在于多进程每个进程拥有独立存储空间,而多线程共享存储空间.对于单核CPU来讲,如果是阻塞操作,或者不耗时非阻塞操作,多进程/线程不会提高效率,这时候多进程/线程最有用的通常是耗时而又非阻塞的I/O操作. 打个比喻,一个人要看两部电影,可以看完一部再看另一部,也可以同时看,看一眼这个暂停,看一眼那个再暂停看回前一个,快速不停切换,你会觉得因为两部一起看所以先看完吗?理论上两部电影播放时间加起来是一样,所以看完所用时间应该一样.但是实际使用时间反而可能第一种方法快,为什么?切换是

linux查看进程的线程数

top -H -p $PID  #查看对应进程的那个线程占用CPU过高 1.top -H 手册中说:-H : Threads toggle 加上这个选项启动top,top一行显示一个线程.否则,它一行显示一个进程. 2.ps xH 手册中说:H Show threads as if they were processes 这样可以查看所有存在的线程. 3.ps -mp 手册中说:m Show threads after processes 这样可以查看一个进程起的线程数. 查看进程 top 命令

[转载]Linux 线程实现机制分析

本文转自http://www.ibm.com/developerworks/cn/linux/kernel/l-thread/ 支持原创.尊重原创,分享知识! 自从多线程编程的概念出现在 Linux 中以来,Linux 多线应用的发展总是与两个问题脱不开干系:兼容性.效率.本文从线程模型入手,通过分析目前 Linux 平台上最流行的 LinuxThreads 线程库的实现及其不足,描述了 Linux 社区是如何看待和解决兼容性和效率这两个问题的. 一.基础知识:线程和进程 按照教科书上的定义,进

Linux 线程与进程,以及通信

http://blog.chinaunix.net/uid-25324849-id-3110075.html 部分转自:http://blog.chinaunix.net/uid-20620288-id-3025213.html 1.首先要明确进程和线程的含义: 进程(Process)是具有一定独立功能的程序关于某个数据集合上的一次运行活动,是系统进行资源分配和调度的一个独立单位.与程序相比,程序只是一组指令的有序集合,它本身没有任何运行的含义,只是一个静态实体.进程是程序在某个数据集上的执行,