Linux 栈耗尽



进程是操作系统进行资源分配的基本单位,线程是CPU进行调度和分派的基本单位。我们通常意义下的进程包括:由二进制代码组成的应用程序、单线程、分配给该应用程序的一组资源(如内存、文件等),栈在wikipedia上叫callstack调用栈,并不是孤立的有进程栈这个说法,线程也有栈,不过大多时间是共享父进程分配的堆内存空间。无论是线程栈还是进程栈,都有一定的大小,空间耗尽将会触发Segment Fault。

线程栈耗尽

ulimit 查看linux系统的各种limit,ulimit -s 代表的线程栈的大小

现代linux系统下是8MB大小,不论在只有一个主线程的进程中,还是包含许多子线程的进程,都是指线程栈大小,默认8MB,pthread_create创建的线程就是对应这个值。
通过一下的程序测试线程大大小:

通过gdb调试,调试时通过查看线程信息,并attach到线程

gdb中vmmap中查看整个进程消耗的地址空间,如下,其中线程栈的地址是通过mmaped分配的


大小为0x00007ffff77f0000 -0x00007ffff6ff000017 = 0x800000 即8M

每次递归调用test()执行的时候,加上栈上压入eip,ebp,每次消耗栈空间为0x1030
共2021次递归调用共消耗2021*0x1030=0x7fcaf0,加上之前进入Thread消耗的栈空间共8M没问题

进程栈耗尽

Linux下进程栈的大小不一定,通常不到线程栈的两倍,在我的测试环境中,进程栈只比线程栈大一丢丢

进程栈大小0x21000,比8M大一点

递归调用的层数能比之前大一点,2028-2029

需要注意一点:

  • 线程栈的大小不指定是固定的8M,但是进程栈的大小链接时分配,大小与不同的OS和版本有关

 

修改进程栈大小

进程栈的大小可以通过设置pthread的属性attribute来修改
下面的测试程序中malloc了一块16M大小的空间用来作为程序的栈

最终程序能够递归执行

4046次

当前线程栈空间大小在0x00007ffff67ef000-0x00007ffff77f0000,16M
从而我们可以大胆猜测pthread分配线程时也是通过malloc或者mmaped(malloc大内存时就是mmaped)分配的stack,是一个写死的值,default=8M
同时我们也可以通过ulimit -s修改OS默认的线程栈大小

总结

  1. Linux 线程栈的大小是固定的,通过unlimt -a查看,unlimt -s修改,默认为8M,与编译链接无关
  2. Linux 进程栈的大小与链接有关,大小比线程栈略大,但不超过其两倍
  3. 无论是进程栈还是线程栈,当栈被耗尽时,俗称”爆栈”时,都会触发段错误segment falut

参考资料

Linux进程栈和线程栈

发表评论