前言

前篇:学习总结与虚拟内存概念基础理解

           学习总结与虚拟内存进阶

什么是进程虚拟地址盒子

程序运行起来,操作系统给这个进程提供一整块巨大的虚拟地址编号空间。 注意:这块只是地址编号,不是真实内存条(物理内存)。 进程看到的全部地址,全是虚拟地址。要访问真实数据,必须经过 MMU、页表转换成物理内存。 操作系统把这一整个大盒子,切割成多个功能不同的区域,也就是我们要学的「虚拟地址空间布局」。


整体空间分层

Linux 下 32 位程序,虚拟盒子从底部(低地址)到顶部(高地址)依次: .text代码段.data数据段.bss段 → 堆 → 栈。

最最顶端还有一块内核空间,用户代码完全不能触碰。

简单比喻: 盒子底部放程序代码、全局变量;中间是堆(malloc 用);盒子最顶上是栈(函数栈帧)。 堆往高处扩张,栈往低处扩张,两者互相朝对方靠近。


.text 代码段

.text存放程序编译完成后的机器指令,就是 CPU 一条一条执行的代码。 像"hello world"这种字符串字面量,也存放在这个区域。 本区域属性:只读。 如果代码强行尝试修改字符串字面量,程序直接崩溃(段错误)。

注意:这里不存放变量,只放指令与常量字符串。


.data 段

.data放在.text 的上方。 专门存放已经赋予初始值,并且初始值不是 0的全局变量、static 静态变量。

c

int g_a = 10;
static int s_a = 20;

g_as_a就放在.data 段。

要点:这些变量的值,编译的时候就确定,存放在 exe 可执行文件里面。程序运行,直接把值加载进内存。


.bss 段

.bss.data 的上方。 专门存放没有初始化,或者初始值等于 0的全局变量、static 静态变量。

c

int g_b;
static int s_b = 0;

上面两个变量放在.bss 段。

重点特性: 磁盘上的 exe 可执行文件不存储一大堆 0,仅仅记录需要多大一块内存。 程序启动的时候,操作系统找到这块虚拟内存,全部自动置 0


区分普通局部变量与static 局部变量

普通局部变量写在函数内部:

c

void test()
{
    int a = 10;        //普通局部变量
    static int b = 10; //static局部变量
}
  • int a = 10;普通局部变量:放在上。函数调用开辟栈帧,函数结束栈帧销毁,变量直接消失。
  • static int b = 10;static 局部变量:不在栈。有初值 10 →放到.data段;如果static int b;,放到.bss段。 static 变量生命周期是整个程序,函数退出不会销毁。


堆(Heap)

堆位于 .bss 的上方。 mallocreallocfree全部操作堆这块虚拟地址空间。

关键要点(结合你之前做的考题):

  1. 堆向高地址方向增长
  2. malloc 仅仅分配虚拟地址不会立刻分配物理内存
  3. 当代码第一次访问这块 malloc 出来的内存,触发缺页异常,操作系统才分配物理页框,建立虚拟‑物理映射。
  4. 堆内存需要程序员手动free释放;忘记释放就产生内存泄漏。


栈(Stack)

栈处在用户虚拟空间的最高位置,就是前面学的函数栈帧所在区域。

  1. 栈向低地址方向增长
  2. 保存:局部变量、函数参数、ebp、返回地址、寄存器现场。
  3. 由 CPU 自动管理:函数进入创建栈帧,函数结束销毁栈帧。不需要手动 free
  4. 栈总大小固定。递归太深、局部数组太大,会栈溢出,程序崩溃。


磁盘 exe 文件 和 内存布局的区别

可执行 exe 是存放在硬盘上的文件。 当程序运行,操作系统把 exe 加载进虚拟内存,但磁盘文件布局 ≠ 内存布局

  1. .text.data:磁盘 exe 里面保存真实数据,运行时拷贝到虚拟内存对应段。
  2. .bss:磁盘 exe 几乎不存数据,仅仅记录需要多大空间;程序启动,在虚拟内存开辟对应大小,操作系统直接全部填 0。
  3. 堆、栈,磁盘 exe 里面完全不存在! 堆、栈是程序跑起来之后,操作系统在虚拟内存中新开辟出来的,exe 文件里没有堆和栈。


完整过程

回顾整套逻辑链:

  1. 程序运行,OS 给进程分配一整套虚拟地址,划分 text /data/bss / 堆 / 栈。
  2. exe 把 text、data 加载到虚拟内存;bss 只分配空间,运行时清零;堆、栈运行时新建。
  3. 此时以上所有区域,仅仅只有虚拟地址,并没有全部映射物理内存
  4. CPU 访问某一块虚拟地址 → MMU 查页表。
  5. 如果没有对应物理页 → 触发缺页异常
  6. 操作系统分配物理页框,建立映射,程序继续运行。

关键点:不管是访问栈局部变量、访问堆 malloc 内存、访问全局变量,只要第一次访问,都有可能触发缺页异常。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐