一. 前言
最近看CSAPP时,对以前没有仔细注意的一处知识盲区产生了兴趣,所以进行了深入研究,并写下此文一记录。
二. 问题
二话不说直接上图。下图是CSAPP第七章的虚拟内存分析图。书中提到
在X86-64位Linux系统中,代码段总是从地址0x400000处开始,后面是数据段。堆在数据段之后,通过调用malloc向上增长……
但是0X400000以下呢?为什么没有提到呢?翻遍全书,都没有一处提到0至0X400000处的空间是做什么用的。实际运行以下cat /proc/self/maps
,发现结果的确如此
经过大量的搜索、查阅文献,最终找到了答案,下面就整理一下,由于Linux和Windows存在不同的情况,因此分开整理。
三. Linux
先来看看这个0X400000的地址在哪儿规定的。实际上在源码/usr/lib/ldscripts/elf_x86_64.x
中可以找到如下定义
1 | PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); \ |
同时,对于X86-64位Linux系统而言,空出低端内存最重要的作用是为了保证空指针可以触发访问缺失页的异常SIGSEGV,而不是访问了不该访问的资源从而导致了奇奇怪怪的问题。可能你回好奇,为什么这里不直接从0x0000001开始做数据段呢?理论上说一个0就够用了呀?
这可以归结为以下几点原因:
- 考虑到多级页表的分配,省一点点不如多省一点,反正64位空间足够用,不再乎几页
- 默认页大小一般是4KB,但是实际会存在很多大页机制,而大页的大小默认是4MB
再追问一个问题,是否能少分配一点呢?答案是当然可以,只要大于65536即可。65536的规定来自于mmap_min_addr
,可以通过/proc/sys/vm/mmap_min_addr
查看。
四. Windows
事情到了windows这里一下就变得不一样了。下图摘自《程序员的自我修养:链接、装载和库》。由图可见,windows操作系统的内存分配十分之奔放,相对于Linux,有着以下特色
- 有多个栈空间供于多线程使用,栈的分布不像Linux一样死板,所以0x400000以下的地方很可能是在使用的
HeapCreate
管理的堆不一定向上增长,而是甚至可能向下增长- 堆最大的大小应如heap5所示,大约为1.5GB-1.7GB
总结
总结一下,对于Linux而言,0X400000以下的空间默认不映射,从而起到保护程序安全的作用。对于windows而言,程序安全交由操作系统保证,因此最大限度利用资源,地址可以低到0X400000以下。
参考文献
[1] CSAPP
[2] 程序员的自我修养:链接、装载和库
[3] /usr/lib/ldscripts/elf_x86_64.x
[4] https://wiki.debian.org/mmap_min_addr
[5]https://stackoverflow.com/questions/65338442/whats-under-0x400000-in-virtual-memory