参考

Linux 进程栈与线程栈大小

http://www.pandademo.com/2015/10/linux-process-and-thread-stack-size/

前段时间和同事一块追一个多线程下栈溢出的bug,究其原因是引用的外部静态库中某个局部的字符串buffer太大导致(1MB),而程序中反复review并没有申请太大的栈空间,当时有怀疑栈空间本身太小,最后通过修改静态库的该buffer大小定义解决,问题看似解决,实则反应对进程和线程的理解不扎实,代码中潜在的问题并没有彻底消除(后来在分析thrift nonblockserver源码时发现是因为默认调用创建IO线程栈指定了大小为1MB),由此本文讨论Linux 下fork或exec调用创建的进程对应栈以及clone调用创建的线程栈。

先从操作系统的角度来复习下基础知识,进程是操作系统进行资源分配的基本单位,线程是CPU进行调度和分派的基本单位。我们通常意义下的进程包括:由二进制代码组成的应用程序、单线程、分配给该应用程序的一组资源(如内存、文件等),一个linux下进程对应的代码中并没有通过NPTL pthred_create显示调用创建线程实际看做只有一个主线程,one thread‘s process,通常多线程编程下默认是依附于该进程的众多线程(当然也可以分离detach);再回过来看栈这个说法,wikipedia上叫callstack调用栈,并不是孤立的有进程栈这个说法,进程只是在分配时通过如随机化方式指定了栈底的初始地址,进程的子线程们有各自的私有栈,可以共享父进程分配的堆内存空间,只有一个主线程的进程也就是有主线程对应的栈,所以栈这个说法通常只有线程栈,并没有明确的进程栈之说,那就更没有所谓的进程栈大小了。

我们通常使用ulimit -s可以看到”进程对应栈“的大小(现代linux系统下通常是8MB大小),不论在只有一个主线程的进程中,还是包含许多子线程的进程,都是指线程栈大小,默认8MB,pthread_create创建的线程就是对应这个值。

再者,根据引用7中的文章,线程栈的空间开辟在所属进程的堆区,线程与其所属的进程共享进程的用户空间,所以线程栈之间可以互访。

refer: 1. https://en.wikipedia.org/wiki/Call_stack  2. http://stackoverflow.com/questions/2131832/whats-the-difference-between-a-threads-stack-and-a-processs-stack  3. http://stackoverflow.com/questions/1762418/process-vs-thread  4. http://stackoverflow.com/questions/13749626/how-to-measure-the-stack-size-of-a-process  5. http://web.stanford.edu/class/cs140/cgi-bin/lecture.php?topic=process  6. 《深入linux内核架构》 7. http://blog.csdn.net/daniel_ice/article/details/8146003

更改Linux默认栈空间的大小

https://blog.csdn.net/u010189459/article/details/27703267

有时候写程序处理大量的数据,需要分配很大的数组来存放一些数据,但有时候分配的数组太大的话运行时会出现断错误。这种情况可能是分配的数组大小超过了linux系统的默认栈空间的大小了,导致栈空间不够分配,出现错误。

(1)查看linux默认栈空间的大小

​ 通过命令 ulimit -s 查看linux的默认栈空间大小,默认情况下为8192 KB 即8M。

(2)临时改变栈空间的大小

​ 通过命令 ulimit -s 设置大小值临时改变栈空间大小。例如:ulimit -s 102400,即修改为100M。

(3)永久修改栈空间大大小。有两种方法:

​ 方法一:可以在/etc/rc.local 内加入 ulimit -s 102400 则可以开机就设置栈空间大小,任何用户启动的时候都会调用。

​ 方法二:修改配置文件/etc/security/limits.conf

进程与线程栈大小的调整

https://my.oschina.net/shelllife/blog/181916

问题现象

首先看一个现象,最近在嵌入式项目开发中发现的,下面是设备的内存总量及使用:

img

总量是24M左右,项目主程序大小1M不到,但是在默认的系统环境设置下,程序运行起来后的top看起来是这样:

img

VSZ的大小是221MB,所以计算出来的内存使用百分比是935.4% = 221MB/24MB.VSZ表示程序使用的总虚拟内存空间大小。在很久之前也曾遇到过同样的现象,只是当时没有去深入了解为什么。刚开始发现这个221MB时,非常地吃惊,无论如何也想不通为什么1M大小不到的程序会需要使用到200M以上的内存空间。

现象分析

程序是一个多线程的程序,而且有不少的线程是由线程再次创建的,系统环境是linux2.6.32的内核。通过对其它单进程的VSZ大小观察,发现VSZ的大小好像与程序使用的线程数目成正比关系。因此想到可以通过使用Posix Pthread库的pthread_attr_setstacksize接口来修改线程栈的大小,于是将20多个线程的栈的大小修改为512KB,虽然有点麻烦,但是再次运行,VSZ的确大幅地减少为30MB左右。

在分析解决问题的过程中,了解到另一个影响应用程序运行栈大小的系统设置:ulimit -s。通过这个命令可以查看系统默认的栈大小以及修改应用运行时的栈大小,默认的8192KB。这里再次分析上面的现象。linux系统中使用clone机制来实现线程,实际上线程就是一个轻量的进程,因此其栈大小依然是遵循系统的ulimit设置来配置的。所以20多个线程的程序在默认8M的栈大小设置下,会使用到200M左右的虚拟内存空间,包括程序的所有栈空间以及数据内存、堆内存和代码内存。

那么,就可以通过ulimit -s命令修改默认的栈大小,从而达到与调用pthread_attr_setstacksize接口一样的目的和效果。使用ulimit -s 512后,主程序使用的VSZ降低为25M左右,这是因为主线程使用的栈大小也被降低。

但是使用ulimit的一个后果就是它会影响到同一环境(同一shell或者终端)下后续启动的所有程序,如果修改成启动时设置的话就会影响到整个系统,这显然不是想要的。有两个方法可以能消除这个影响:

  1. 为需要修改栈大小的程序单独编写一个shell脚本,在程序启动前调用ulimit -s。因为子shell的环境不会影响到父shell,所以设置不会改变外部环境。
  2. 在程序运行前执行ulimit -s修改需要的栈大小,在程序运行后再次执行ulimit -s修改回原来的栈大小。

PS:虽然降低了程序使用的虚拟内存的大小,但是我还是有一个很大的疑问:

程序使用200M多的虚拟内存和使用20M多的虚拟内存,运行效果没有什么变化,好像没有带来什么有用的性能改善。我能想要的“好处”就是系统在进行地址转换和页面管理时会高效一点,但难道不应该有一些更重要的性能提升吗,不然除了让top内容中的VSZ和%MEM栏更好看合理点外,没必要去费精力调整?期待有人能帮忙解惑。