Linux的线程创建限制

导言:现网有个版本上线一段时间后,通过SSH重新登陆时显示Cannot allocate memory错误,结果只能通过重启机器来恢复服务。通过分析发现是新的版本引入一个bug,某个进程会不停地创建新的线程,那么问题是Linux下一个进程可以创建多少个线程,从而会导致这个错误出现。

1 原因分析

问题重现,通过一个测试程序,验证错误是否会重现。

#include <iostream>
#include <pthread.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
using namespace std;

void* thr_func (void*)
{
    for(;;) sleep(500);
    return 0;
}

void max_thread_test ()
{
    const int MAX = 4194304;
    //const int MAX = 10;

    int i = 0;
    for (; i < MAX; ++i) {

        int ret = 0;
        pthread_attr_t attr;
        pthread_attr_init(&attr);

        // detached
        ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
        if (ret) { 
            perror("pthread_attr_setdetachstate err"); 
            exit(1);
        }

        size_t stacksize = 0;
        ret = pthread_attr_getstacksize(&attr, &stacksize);
        if (ret) { 
            perror("pthread_attr_getstacksize err"); 
            exit(1);
        } else {
            static bool once = true;
            if (once) {
                std::cout << "default thread stacksize: " << stacksize << std::endl;
                once = false;
            }

        }

        // thread stacksize
        stacksize = 16384;// 16KB
        ret = pthread_attr_setstacksize(&attr, stacksize);
        if (ret) { 
            perror("pthread_attr_setstacksize err"); 
            exit(1);
        } else {
            static bool once = true;
            if (once) {
                std::cout << "change thread stacksize to : " << stacksize << std::endl;
                once = false;
            }
        }

        if (!(i % (MAX / 10000))) std::cerr << "..." << i;

        pthread_t thread;
        ret = pthread_create(&thread, &attr, thr_func, NULL);
        if (ret) {
            std::cerr << "\nthreads created " << i << std::endl ;
            perror("pthread_create");
            exit(1);
        }
    }

    std::cerr << "threads created " << i - 1 << std::endl ;
    std::cout << "exit (succes)" << std::endl;
}

int main()
{
    max_thread_test();
    for(;;) sleep(5000);

    std::cout << "end" << std::endl;
    return 0;
}
/* 在Mac上使用LLVM/Clang编译得到如下结果:

$./test_max_thread 
default thread stacksize: 524288
change thread stacksize to : 16384
...0...419...838...1257...1676
threads created 2047
pthread_create: Cannot allocate memory

* */

通过max-thread-per-process-in-linuxMaximum number of threads per process in Linux?可以得知,除了内存大小限制外,还有很多内核参数的限制,但理论上一个进程可以创建的最大线程数量为:

number of threads = total virtual memory / stack size

因此,32位系统由于进程的虚拟地址空间为4G,可以创建的线程数量为3G/8M=384个线程,而64位系统的进程可以创建的线程数量为128T/8M=16777216个线程(千万级)。当然在实际使用中一般不会创建这么多线程,因为线程间的切换会占用2000+的CPU时钟周期,可能导致CPU过高。一般会根据CPU核数启用相应的线程数,尽量减少不必要的线程切换。

pic

通过修改下面的参数,可以尽可能地创建更多的线程。

echo 1 > /proc/sys/vm/overcommit_memory 
echo 1000000 > /proc/sys/kernel/threads-max
echo 10000000 > /proc/sys/vm/max_map_count
echo "4194304" > /proc/sys/kernel/pid_max

max_map_count的补充说明:在Linux上测试,创建一次线程操作会调用一次mmap,但是会映射两个地址段(/proc/pid/maps有两行,一个4KB,一个8MB),map_count会累积两次,所以一个进程要满足:创建线程数*2 + 其他的映射地址段数量 < max_map_count

2 Refer

man 5 proc
理解Linux的memory overcommit
Infographics: Operation Costs in CPU Clock Cycles
http://www.kegel.com/c10k.html#limits.threads
https://www.akkadia.org/drepper/glibcthreads.html
https://www.akkadia.org/drepper/nptl-design.pdf
NPTL caps maximum threads at 65528?

©️2020 CSDN 皮肤主题: 大白 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值