虚拟地址空间

现在我们将介绍linux 32位的虚拟地址空间,这是我们编程进阶的一个标志,4G虚拟地址空间的虚拟地址空间是每个进程的真实写照,堆区,栈区,数据区等等都将在此见证。首先我们先引入操作系统的虚拟内存:

一般而言,我们日常聊起的4G,8G的电脑都是基于RAM(随机存储器)的容量而言的。RAM由SRAM和DRAM组成。一般而言SRAM负责cpu的LV1,LV2,LV3 cahce高速缓存。用DRAM来缓存表示虚拟内存系统的缓存,它在主存中缓存虚拟页。虚拟内存对实际内存有保护作用。
    linux为每一个进程维护一个单独的虚拟地址空间,如图所示。我们在哪都可以见到这一幅图,它包含了外卖熟悉的代码,数据,堆,共享库还有栈区。
    虚拟内存包含内核中的代码和数据结构。内核虚拟内存的某些区域被映射到所有进程共享的物理页面。当然,我们的linux也将一组连续的虚拟页面(大小等于系统的DRAM的总容量)映射到相应的一组连续的物理页面中。这为内核提供一种便利的方法来访问物理内存中的任何位置。
内核虚拟内存的其他区域包含每个进程都不相同的数据。比如,页表,内核的上下文中指向代码时使用的栈,以及记录虚拟地址空间组织的各种数据结构。

Linux将虚拟地址空间分为若干个段来管理。各段信息由ELF文件的文件头中的段表来记录,段表包含了各个段的偏移量和大小。每个段都是已经存在着的虚拟内存连续片(chunk),这些页以某种方式相互关联起来。比如说:代码段,数据段,堆,共享库段,以及用户栈都是不同的区域。每个存在的虚拟页面都保存在某个区域中,而不属于某个区域的虚拟页是不存在的,并且不能被进程引用。区域的概念很重要,因为它允许虚拟地址空间有间隙。内核不必去记录那些不存在的虚拟页,而这样的页不用占用内存,磁盘或者内核本身中的其它额外资源。


回到4G虚拟内存空间,分为两部分:用户空间3G+1G的内核空间,其中用户的前128M是保留的,意义在图中表示了。为了防止小整数的操作干扰还有页的加载约定。

介绍一下各段
Text segment(指令代码段):存放的是程序的指令。各个函数的入口地址。可执行
Data segment(数据段):数据段存放的是程序中 初始化的全局变量,静态变量(static修饰,不管局部和全局)
数据段还可以分为可读数据段和只读数据段(在ELF可执行文件的生成过程中,这两个段被合并了),可读数据段存储以上数据,可读可写;只读数据段(.rodata)存储常量诸如const修饰的变量,还有类似char *p="hello",以及printf中的格式化字符串,还有switch的跳转表;其中hello这个字符串被保存在只读段,这是为什么不能单独修改它里面的个别元素的本质原因。
.BSS segment(Better save space):保存未经过初始化的全局变量,静态变量以及初始化为0的全局变量,静态变量。在运行时为这些变量分配空间。说到.BSS要略微提一下COMMONt符号,COMMON是符号的特殊伪节,有三个ABS表示不该被重定位的符号,UNDEF代表未定义的符号,也就是在本模块中引用,而在别的地方定义的符号;COMMON符号代表未被分配位置的未初始化的数据目标。当然这些东西只有在可重定位文件中才有,,可执行文件是没有的。comment和.bss的区别很细微。但是现在的gcc版本根据:COMMNON为未初始化的全局变量,.bss为未初始化的静态变量,以及初始化为0的全局或者静态变量。来将可重定位目标文件中的符号分配到COMMON和.bss段中。
.Heap,堆区是程序动态内存管理的区域,一般的malloc,free,new,delete都在这一块区域行动。当然对于堆区的资源。在申请使用结束后必须手动释放,否则会发生内存泄漏。当然堆区的生长方向是由低地址向高地址生长。堆区有两个重要的系统调用brk和sbrk希望大家下去了解。

在堆区和栈区之间有一块空间,为共享库的映射空间,共享库在需要的时候被调入此块空间。

图中还有一块内存映射区,主要针对于mmap调用,当malloc申请小于128k字节的空间使用brk调用获得相应的区域,对于大于128k的动态内存申请,malloc会使用mmap映射一块匿名内存区域,就是这一块区域。

.Stack栈区,在运行时负责函数的形参,局部变量的管理,主要由esp和ebp寄存器记录当前栈顶和当前栈低的函数调用管理。栈的起始地址和堆的起始地址不固定:防止栈溢出或者是程序的攻击。

在栈区后面将会存储命令行参数和环境变量

从0xC000 0000H开始进入高端内核区,该区又分为ZONE_DMA,ZONE_NORMAL和ZONE_HIGHMEN,具体作用如图。

最后以一个题目来考察大家对于虚拟地址空间的掌握程度
请说明下面表达式的值,在内存的哪个区域:
 
&p:___, p:____, &p1:___, p1:____,
 
p2:___, p3:___, p4:____, p5:____.
 
void (*p)(char) = foo;
 
void foo(char a)
{
static char nCount;
char p1[] = “AAA”;
char *p2  = “AAA”;
char *p3  = (char *)malloc(10);
char *p4  = &a;
char *p5  = &nCount;
}
 
A.数据段(全局变量区)    B.代码段
C.栈     D.堆   E.不一定,视调用情况而定。

作图:


  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值