在《算法设计与分析》这门课里学了递归,但老师只是提了一嘴”递归不能太深,否则会栈溢出”。那得多深才溢出啊?为啥会溢出?得找找资料看看了。下面是我的一些笔记,程序地址空间、进程地址空间……

测试环境centos7 x64。

程序地址空间

根据书上、网上的资料画个图,右图来源linux.cn。

在x86体系结构中分段机制是必选的,而分页机制则可由具体的操作系统而选择,Linux通过让段的基地址为0而巧妙的绕过了基地址。因此,对于Linux来说,虚地址和线性地址是一致的。在32位的平台上,线性地址的大小为固定的4GB。并且,由于采用了保护机制,Linux内核将这4GB分为两部分,虚地址较高的1GB(0xC0000000到0xFFFFFFFF)为共享的内核空间;而较低的3GB(0x00000000到0xBFFFFFFF)为每个进程的用户空间。由于每个进程都不能直接访问内核空间,而是通过系统调用间接进入内核,因此,所有的进程都共享内核空间。而每个进程都拥有各自的用户空间,各个进程之间不能互相访问彼此的用户空间。因此,对于每一个具体的进程而言,都拥有4GB的虚拟地址空间。——百度百科

看到上图中(linux.cn的那张图),说栈有8MB,注意是e.g,linux下使用ulimit 命令可以查看系统的很多上限值。

那就来看看啵,ulimit -a,列出看看。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[parallels@centos-7 vimExercise]$ ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 7218
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192 # 栈
cpu time (seconds, -t) unlimited
max user processes (-u) 4096
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
[parallels@centos-7 vimExercise]$

他说我现在虚拟机的stack size是8912kb,也就是8MB。那试试咯,试试就试试。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>

int cnt = 0;

void func() {
char a[1024 * 1024];
(void) a;
cnt++;
printf("%d MB.\n", cnt);
func();
}

int main() {

func();

return 0;
}

程序输出

1
2
3
4
5
6
7
8
9
10
[parallels@centos-7 vimExercise]$ ./stackSizeTest 
1 MB.
2 MB.
3 MB.
4 MB.
5 MB.
6 MB.
7 MB.
Segmentation fault (core dumped)
[parallels@centos-7 vimExercise]$

是的,到了7MB再不能申请1MB大小的栈空间了,因为函数栈帧以及一些别的变量占去了一部分。

然后我用ulimit -s 2048,修改栈的限制为2048kb,试试,不出所料,1MB。

1
2
3
4
[parallels@centos-7 vimExercise]$ ./stackSizeTest 
1 MB.
Segmentation fault (core dumped)
[parallels@centos-7 vimExercise]$

哈哈,以后你再说栈溢出我就改成unlimited。

注意

好吧,刚才改ulimit 发现改不回来了哈哈哈,它提示我不允许的操作。。。。额。。。。吓尿了,赶紧sudo,然后提示没有这条命令????

好吧,百度ing……试了煤球用。抱着试一试的心态,先su,然后ulimit,我靠成了就。

百度的过程中学到了,这个值不能指定的太大,否则当并发运行的线程数多时,系统的内存会被众多thread耗尽。

大大大,很大。能申请多少取决于电脑内存,测试的时候我电脑16GB,申请10GB没啥毛病(没别的占用的情况下,一般系统有你申请他就给你了)。

那么写代码的时候是多多在栈上弄好,还是堆上?

如果是小对象,并且需要频繁创建和销毁,在栈上更好。在栈上分配内存更加高效(修改esp寄存器)。
如果是大对象必须在堆上分配。
如果是很大的对象,又要频繁使用,那就上内存池吧。


剖析内存中的程序之秘 https://linux.cn/article-9255-1.html?pr