虚拟地址空间布局——基于x86体系32位下系统

我们知道,一个可执行文件运行起来的话它就变成了进程。在x86 32bit Linux下,我们的系统会给每个进程分配一个4G大小的虚拟地址空间。你可能会说,我电脑总共就4g内存这每个进程都给4g怎么可能啊。注意虚拟两个字。那么这块空间里都装了什么东西呢?

其中前3G内存是用户空间,最后1G是内核空间。所有的进程有各自的用户空间,但所有的进程都共享一个内核空间。不要着急,我们从上到下挨个来分析。

假设这段虚拟地址空间是从-0x00000000开始的。

那么首先是用户空间:

①128M的不可访问区域

如果我们定义了一个指针为NULL到最后忘记赋值,却使用了strcpy之类的函数就会出错,原因就是你妄图对这块不可访问区域做些什么。即是0x00000000到0x80480000这段空间。其实在128M下面紧接着还有一点空间也是属于不可访问区域,防止.text段的数据越界。所以.text段并不是严格意义上和不可访问区域紧密连接的。

②.text代码段

这段存储的是你编写的代码中的指令。什么是指令?一份代码是由数据和指令组成的,除了数据之外都叫做指令。同时,局部变量也属于指令,但是局部变量存储在栈上。

③.data数据段

扫描二维码关注公众号,回复: 2340904 查看本文章

这段存储的就是代码中的各种数据了,包括全局变量,静态全局变量,静态局部变量。但是需要注意的是,必须是已经赋值且赋值不为0的数据才存储在该段中!

④.bss段

它存储的也是数据,不过是未赋值或者赋值为0的数据了。这是一个不占文件空间的段,具体在下面.obj文件组成部分有讲解。

⑤空洞

这是给堆预留的空间

⑥栈

所有的局部变量都存储在这里且函数的运行也需要栈,需要注意的是上面是栈底下面是栈顶(当前x8632位系统是小端模式,低字节存储在低地址。关于大小端的判断可以看我的另一篇博客)

⑦命令行参数

我们在写main函数的时候一般都是这么写:

int main()
{
    return 0;
}

或
int main(int argc,char **argv)
{
    return 0;
}

其实它的原型长这样:

int main (int argc, char** argv, char**environ)

前两个参数大家应当都很熟悉,argc是命令行参数的个数,不指定时默认为1,是当前文件名,char **argv也可以写成char *argv[],其中argv[0]默认保存的就是argc==1时的程序名。如果在执行程序时后面跟随指令,则argc的值和argv[]中的元素就会发生变化。然而这第三个参数是什么意思呢?在我们调用一些系统库函数的时候,我们需要使用预处理指令#include。但是我们系统怎么知道去哪个路径寻找这些头文件的函数定义呢?就是通过char **environ(环境变量)来实现的。

话说回来,命令行参数就是我们的argv了。里面存放着我们的程序名,以及参数(如果你给了的话)。

⑧环境变量

就是在第⑦条中介绍的,main函数的第三个参数啦。

接下来我们来看内核空间:

①ZONE_DMA(16MB)

全名ZONE_DirectMemoryAccess(直接内存访问),可以加快磁盘和内存之间数据的交换,不需要经过CPU的寄存器,这时CPU可以去干别的事,大大增加了效率。

②ZONE_NORMAL (892MB)

内核中最重要最常用的部分。

③ZONE_HIGHMEM (128MB)

所谓的高端内存,用于在内核中映射高于1G的物理内存时使用。64位系统则没有该段(根本不需要,因为64位操作系统给内核空间分配的内存达到512G)。

猜你喜欢

转载自blog.csdn.net/czc1997/article/details/81159126
今日推荐