一个进程最多创建多少个线程?
由于每个线程都有自己独立的栈空间,因此,创建的线程数量会受到栈空间大小的限制
我们使用 ulimit -s 查看栈的大小:
[root@localhost test]# ulimit -s
8192
对于 32 位 OS 来说,用户空间的虚拟内存最大为 3G,那么,理论上一个进程能创建的线程数量为 3G / 8192K = 384 个
但是,实际数量通常还会更低,一个线程所需的虚拟内存空间应该大于 8M(包括了 TLS,以及内核管理的 TCB)
做个实验:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
void *task(void *arg)
{
while (1)
{
sleep(100);
}
}
int main(void)
{
int cnt = 0, err = 0;
pthread_t tid;
while (err == 0)
{
err = pthread_create(&tid, NULL, task, NULL);
++cnt;
}
printf("pid: %d\ntotal thread number: %d\nerror: %s", getpid(), cnt, strerror(errno));
getchar();
}
由于我没有 32 位的环境,从网上扒了一个别人的结果:
大约为 300 个
对于 64 位 OS,用户虚拟内存空间通常为 128T,理论上可以创建千万级别的线程
但是实际并不是如此,线程数还取决于:
/proc/sys/kernel/threads-max,表示系统支持的最大线程数/proc/sys/kernel/pid_max,表示系统全局的 PID 号数值的限制,每一个进程或线程都有 ID,ID 的值超过这个数,进程或线程就会创建失败;/proc/sys/vm/max_map_count,表示限制一个进程可以拥有的 VMA(虚拟内存区域)的数量,超过该数量,就无法为该进程分配内存了
在我的环境(64 位,2 Cores,4G RAM),运行结果如下:
[root@localhost test]# cat /proc/sys/kernel/threads-max
29918
已经达到系统全局支持的最大线程数了
总结一波:
- 进程创建的线程数与 虚拟内存空间大小、栈空间大小 有关
- 对于 64 位 OS,由于虚拟内存空间很大,基本上不存在虚拟内存不足导致线程无法创建
- 对于 64 位 OS,限制线程的最大创建数的主要原因取决于:
/proc/sys/kernel/threads-max、/proc/sys/kernel/pid_max、/proc/sys/vm/max_map_count
一个线程崩溃了,所属进程也会崩溃吗?
在 C/C++,默认是这样的
看一个代码:
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
void *task(void *arg)
{
sleep(1);
int *p = NULL;
*p = 1; // null pointer error
printf("sub thread exit\n");
return NULL;
}
int main(void)
{
pthread_t tid;
pthread_create(&tid, NULL, task, NULL);
pthread_join(tid, NULL);
printf("main thread exit\n");
}
运行结果:
[root@localhost test]# clang test.c -o test -pthread
[root@localhost test]# ./test
Segmentation fault (core dumped)
可以发现,主线程还没有输出 main thread exit,进程就退出了
事实上,崩溃的本质是内核向进程发出了 SIGSEGV 信号,而 SIGSEGV 是段错误
由于线程之间(包括主线程)共享 mm_struct,内核认为如果出现段错误,如果进程不退出,可能会造成更严重的后果
我们可以注册一个信号处理函数,单独处理 SIGSEGV 信号:
void sigHandler(int sig)
{
printf("Catched SIG_%d\n", sig);
if (sig == SIGSEGV)
{
// 可以记录一下日志。。。
exit(-1); // 如果是段错误,退出进程
}
}
int main(void)
{
signal(SIGSEGV, sigHandler);
// ...
}
输出:
[root@localhost test]# ./test
Catched SIG_11