Linux —— 虚拟进程地址空间

发布于:2025-09-08 ⋅ 阅读:(31) ⋅ 点赞:(0)

在这里插入图片描述
在这里插入图片描述


🎁个人主页:工藤新一¹

🔍系列专栏:C++面向对象(类和对象篇)

🌟心中的天空之城,终会照亮我前方的路

🎉欢迎大家点赞👍评论📝收藏⭐文章


虚拟进程地址空间

虚拟地址空间 是指一个 OS为每个运行中的进程(程序)提供的一个抽象的、独立的、连续的逻辑地址范围。这个空间是 “虚拟的”,这并不意味着物理内存中真的有这么一大块连续的区域,而是通过硬件(MMU,内存管理单元)和 Kernel 的协作,将虚拟地址映射到分散的物理内存页上

在这里插入图片描述


一、虚拟地址空间经典布局

一个进程的典型内存地址空间布局如下图所示,它被划分为多个具有不同权限(读、写、执行)的段(Segments):

从高地址到低地址:

  1. 内核空间(Kernel Space)
    • 通常位于地址空间的最高处(例如,在32位Linux中,0x C000 0000以上)。
    • 存放操作系统内核的代码、数据和数据结构。
    • 所有进程共享同一份内核映射。但这段空间受保护,用户态进程无法直接访问,必须通过系统调用(Syscall)进入内核态才能访问。
  2. (Stack)
    • 向下增长(向低地址方向)。
    • 用于存储局部变量函数参数返回地址等。
    • 每个函数被调用时,会在栈上分配一个新的“栈帧”。
    • 它的增长是自动的,但大小有限(通常默认为几MB),溢出会导致“栈溢出”错误。
  3. 内存映射段(Memory Mapping Segment)
    • 用于映射文件或匿名内存。
    • 动态链接库(如 .so.dll 文件)就加载在这里。
    • 也可以通过 mmap() 系统调用创建,用于大块内存的分配或进程间共享内存。
  4. (Heap)
    • 向上增长(向高地址方向)。
    • 用于动态内存分配。当程序员使用 malloc()new 等申请内存时,内存就从这里分配。
    • 堆的大小只受限于系统可用的虚拟内存总量,管理由程序员负责(分配和释放), improper management leads to memory leaks.
  5. BSS 段(.bss)
    • 存放未初始化的全局变量和静态变量
    • 在程序开始执行前,操作系统会将此段初始化为零。
  6. 数据段(.data)
    • 存放已初始化的全局变量和静态变量
  7. 代码段(文本段)(.text)
    • 存放程序的执行代码(机器指令)。
    • 通常是只读和可执行的,以防止代码被意外修改。
  8. 保留区(Reserved)
    • 通常是最低地址的一段空间(例如 0x00x400000),不允许访问,用于捕捉空指针等错误。

地址区域分布:

在这里插入图片描述


在这里插入图片描述


感受虚拟地址:

fork():一次调用,两次返回

这是理解 fork() 最关键也是最反直觉的一点:

  • 在父进程中,fork() 返回新创建子进程的进程ID(PID)(一个大于0的数)。
  • 在子进程中,fork() 返回 0
  • 如果创建失败(例如系统资源不足),fork() 返回 -1

在这里插入图片描述


进程具有独立性:数据层面上,互不影响;此时就需写时拷贝,实现进程的个性化

在这里插入图片描述

相同地址,获取不同变量值


二、页表

2.1 核心定义:什么是页表?

页表是虚拟内存系统的核心数据结构,是连接 虚拟地址物理地址 的“地图”或“翻译官”;是 Kernel 为每个进程维护的一个映射表,它记录了该进程的虚拟内存页对应到物理内存帧映射关系

简单来说,它的工作就是回答这个问题:

“这个进程看到的虚拟地址 X,实际上在物理内存的哪个地方?”


在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


在这里插入图片描述

OS 会将进程物理地址隐藏起来,我们只能观测到进程的虚拟地址


解决历史遗留问题:

在这里插入图片描述


2.2 为什么需要页表?

页表是实现虚拟地址空间这一抽象概念的技术基础。没有页表,虚拟内存就无法工作。它的存在是为了:

  1. 实现地址翻译:将程序使用的 虚拟地址转换 为硬件使用的 物理地址
  2. 实施内存保护:通过页表项中的权限位,控制进程对内存的访问(可读?可写?可执行?)。
  3. 支持“换出”到磁盘:通过页表项中的“存在/不存在”位,操作系统可以知道某页是否在物理内存中,如果不在,它的数据存放在硬盘的哪个位置。

三、虚拟地址空间

3.1 虚拟地址空间是什么?

  • **虚拟地址空间本质上就是 OS 给进程画的一张饼! **《大富翁例子》 让进程误以为其独占整个内存的相关资源

画大饼的作用:让每一个进程都认为自己有 4GB的物理内存空间,或者:让每一个进程都认为自己在独占物理内存空间

OS 为每个进程都画了大饼,所以我们也要把这张大饼管理起来


3.2 如何管理虚拟地址空间?

先描述,在组织!

将所有的 虚拟进程地址空间[饼],用链表的方式管理起来;因此,对饼的管理就转化为了对链表的增删查改

虚拟地址空间本质:Kernel当中为进程创建的结构体对象!

在这里插入图片描述


在这里插入图片描述


在这里插入图片描述

在这里插入图片描述


3.3 为什么要有虚拟地址空间?

1. 将无/乱序的物理地址转变为有序的虚拟地址!

  • 对用户(进程)而言:虚拟地址空间是连续且有序的
  • 对系统(操作系统)而言:物理内存[完全随机且不连续]的分配是灵活且混乱的

在这里插入图片描述


2. 地址转化过程中对地址与操作进行合法性判定,进而保护物理内存!

在这里插入图片描述


a. 什么是野指针?

在这里插入图片描述


b. char* str = “hello linux!”; *str = ‘H’;为什么在字符常量区写入就会崩溃?

在这里插入图片描述


在这里插入图片描述


3. 缺页中断 与 按需调页

缺页中断是操作系统 “欺骗” 进程的基础,也是它管理内存的得力工具(画大饼)

在这里插入图片描述


4. 使进程管理与内存管理,进行一定程度的解耦合

在这里插入图片描述


在这里插入图片描述

进程管理 & 内存管理 –——> 直线脱钩


四、工业疑问

a. 创建进程时,是否可以只创建 PCB/地址空间/页表?

核心观点:惰性加载 (Lazy Loading) 与按需调页 (Demand Paging)

问题背后隐藏着一个关键思想:为什么要在进程一开始就把它可能永远用不到的东西全部加载好呢? 这太浪费了….它们遵循 “惰性”原则,只在真正需要时才分配资源。这个过程就是通过 **缺页中断 ** 来实现的


在这里插入图片描述


在这里插入图片描述


b. 创建进程现有task_struct,还是先加载数据/代码

一定先有管理结构task_structmm_struct,空页表[虚物]。滞后加载代码和数据[实物],发生在第一次访问时,由缺页中断机制驱动。并且,加载过程本身也是由这些数据结构指导

在这里插入图片描述


c. 如何理解进程挂起?

在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


五、虚拟内存管理

虚拟内存管理是一种内存抽象机制。虚拟内存管理系统的任务,就是将进程使用的这些虚拟地址(Virtual Address)动态地映射到物理内存上的物理地址(Physical Address),或者必要时映射到磁盘上的交换空间(Swap Space)

这个过程主要由计算机的 内存管理单元(MMU) 和操作系统内核共同完成。


描述 Linux 下进程的地址空间的所有的信息的结构体是 mm_struct (内存描述符)。每个进程只有⼀ 个 mm_struct结构,在每个进程的 task_struct结构中,有⼀个指向该进程的结构

在这里插入图片描述

struct mm_struct
 {
	/*...*/
	struct vm_area_struct *mmap; /* 指向虚拟区间(VMA)链表 */        
	struct rb_root mm_rb; /* red_black树 */                
	unsigned long task_size; /*具有该结构体的进程的虚拟地址空间的⼤⼩*/             
/*...*/
 // 代码段、数据段、堆栈段、参数段及环境段的起始和结束地址。

    unsigned long start_code, end_code, start_data, end_data;
	unsigned long start_brk, brk, start_stack;
	unsigned long arg_start, arg_end, env_start, env_end;
 /*...*/

  • Virtual Memory

在这里插入图片描述


在这里插入图片描述


进程具有独立性:

  1. 内核数据结构独立!
  2. 加载进内存的代码和数据独立!

在这里插入图片描述


目前,我们对于虚拟地址空间的理解只能做到局部性的逻辑自洽


🌟 各位看官好我是工藤新一¹呀~

🌈 愿各位心中所想,终有所致!
在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到