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 线程(进程)数限制分析的更多相关文章

  1. (转)Linux 最大进程数

    Linux 最大进程数  原文:https://www.cnblogs.com/pangguoping/p/5792075.html 前言 使用环境:centos 7系统 一.查看用户打开的最大进程数 ...

  2. Linux记录-进程数和句柄数调整

    1.cat /etc/security/limits.confwebuser soft nofile 65535webuser hard nofile 65535webuser soft nproc ...

  3. linux最大进程数、最大打开文件数

    ulimit 是一种 linux 系统的内键功能,它具有一套参数集,用于为由它生成的 shell 进程及其子进程的资源使用设置限制.本文将在后面的章节中详细说明 ulimit 的功能,使用以及它的影响 ...

  4. Linux 最大进程数

    前言 使用环境:centos 7系统 一.查看用户打开的最大进程数 ulimit -a max user processes              (-u) #系统限制某用户下最多可以运行多少进程 ...

  5. linux最大进程数

    使用 ulimit -a 命令,查看 max user processes 的输出,就是系统最大进程数 core file size (blocks, -c) unlimited data seg s ...

  6. linux打开进程数测试

    查看linux默认打开最大打开进程数 具体参考:https://www.jb51.net/article/143667.htm #include <unistd.h> #include & ...

  7. linux查看进程数

    命令行: $ ps -ef | wc -l 如果想匹配某个关键词的话,加上grep,下面命令是匹配关键词 “XXX”,并统计含有该关键词的进程数 $ ps -ef | grep XXX | wc -l

  8. LINUX最大线程数及最大进程数

    查看最大线程数: cat /proc/sys/kernel/threads-max ulimit User limits - limit the use of system-wide resource ...

  9. linux查看进程的线程数

    top -H -p $PID  #查看对应进程的那个线程占用CPU过高 1.top -H 手册中说:-H : Threads toggle 加上这个选项启动top,top一行显示一个线程.否则,它一行 ...

随机推荐

  1. [iOS翻译]《iOS 7 Programming Cookbook》:iOS文件与文件夹管理(上)

    简介: iOS基于OS X,而OSX本身基于Unix操作系统.在iOS里面,操作系统的完全路径结构是不可见的,因为每个APP的数据都存储自身的沙盒里面.沙盒环境实际上听起来像这样:一个只允许当前APP ...

  2. MATLAB中提高fwrite和fprintf函数的I/O性能

    提高fwrite和fprintf函数的I/O性能 http://www.matlabsky.com/thread-34861-1-1.html     今天我们将讨论下著名的fwrite(fprint ...

  3. MMDrawerController第三方库的使用(根据导航item+滚动条progressView实现的手势滑动切换视图的)

    https://github.com/mutualmobile/MMDrawerController MMDrawerControlleris边抽屉导航容器视图控制器用来支持越来越多的应用程序利用抽屉 ...

  4. iOS开发工具(上班需要必备的软件)

    1 源代码管理工具 SVN:SVN可以使用的客户端软件有Cornerstone,SmartSVN,svnX,乌龟SVN,莲花版svn等等 或者git(sourcetree) 2 有道词典 3 Foxm ...

  5. Android开发之高效加载Bitmap

    一.概述 在Android开发中,我们经常与Bitmap打交道,而对Bitmap的不恰当的操作经常会导致OOM(Out of Memory).这篇文章我们会介绍如何高效地在Android开发中使用Bi ...

  6. Angularjs 异步模块加载项目模板

    ng-lazy-module-seed(Angularjs 异步模块加载项目模板) 相信做过SPA项目的朋友都遇到过这个问题:页面初始化时需要加载文件太大或太多了,许多文件加载后很可能不会运行到,这是 ...

  7. 微信小程序内测申请

    想申请微信小程序的内测?别做梦了! 小程序内测是邀请制的,目前就发放了200个内测邀请.正因为稀缺,江湖传言内测资格已经炒到300万(一套房)一个了 但是!!!!你可以先熟悉一下相关资料和文档,下载一 ...

  8. 【BZOJ 1019】【SHOI2008】汉诺塔(待定系数法递推)

    1019: [SHOI2008]汉诺塔 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 559  Solved: 341[Submit][Status] ...

  9. 第二十课:js中如何操作元素的属性系统

    本章的内容有点复杂,我将用简单的方式来介绍重要的东西,不重要的东西,这里就不讲了,讲了也毛用. 通常我们把对象的非函数成员叫做属性.对元素节点来说,其属性大题分为两大类,固有属性和自定义属性.固有属性 ...

  10. 每天一个linux命令(9):nl命令

    nl命令在linux系统中用来计算文件中行号.nl 可以将输出的文件内容自动的加上行号!其默认的结果与 cat -n 有点不太一样, nl 可以将行号做比较多的显示设计,包括位数与是否自动补齐 0 等 ...