测试环境:
Linux centos-7.shared 3.10.0-693.5.2.el7.x86_64 #1 SMP Fri Oct 20 20:32:50 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
一个进程的虚拟地址空间一般可以大致划分为代码区(text)、只读数据区(rodata)、初始化数据区(data)、为初始化数据区(bss)、堆(heap)、共享内存区(.so,mmap的地方)、栈(stack)、内核区(kernel)。
对于 Linux 进程或者说主线程,其 stack 是在 fork 的时候生成的,实际上就是复制了父亲的 stack 空间地址,然后写时拷贝 (cow) 以及动态增长。
然而对于主线程生成的子线程而言,其 stack 将不再是这样的了,而是事先固定下来的。
线程栈不能动态增长,一旦用尽就没了,这是和生成进程的 fork 不同的地方。
==线程(非主线程)的栈的大小是固定的==,其会在空闲的堆(堆顶附近自顶向下分配)或者是空闲栈(栈底附近自底向上分配),因此线程栈局部函数中分配的变量是存放到各自分配的栈空间,因此可以说是线程私有的,又因为该线程栈的边界是设定好的,因此该线程栈的大小的固定的。
测试
ulimit -a
查看操作系统的相关限制:
可以看到 stack size 的限制是 8192kb 也就是 8MB。
注意这里的 8MB 是指每个被创建的 thread 的 stack 都是这么大。
1 | [parallels@centos-7 LinuxCode]$ ulimit -a |
测试代码:
创建了 3 个 thread,执行 ThreadEntry。
编译后跑起来!
1 |
|
==从 heap 的顶部向下分配。==ps aux | grep a.out
查看 pidcat /proc/[pid]/maps
这个显示进程映射了的内存区域和访问权限。
可以看到:在 heap 下面连续的几个属性为 rw-p
的地址大小刚好都为 8192kb。并且每个都在边界穿插了一个大小为 1000H(4096kb) 的边界空间。
==从 stack 底部向上分配==ulimit -s unlimited
设置 stack size 为 unlimited,注意虽然设置了stack size为无限,但是实际上其并不是无限的,而也是固定大小的线程栈,大小为1mb。
然后 cat /proc/[pid]/maps
查看虚拟地址空间的映射。
可以看到,这种情况下线程栈是分配在 stack 底附近,自底向上生长的。
结论
==不管线程栈是在堆分配还是在栈分配,其都是固定大小的,有边界的。==
参考:
https://blog.csdn.net/qq_16097611/article/details/82592873
https://blog.csdn.net/yangkuanqaz85988/article/details/52403726
https://blog.csdn.net/lijzheng/article/details/23618365
EOF