news 2026/5/27 13:10:20

【Linux】初步构建框架—虚拟地址空间(二)—mm_struct结构体揭秘与页表标志位

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【Linux】初步构建框架—虚拟地址空间(二)—mm_struct结构体揭秘与页表标志位

书接上回,我们已经对虚拟地址空间、页表以及“懒加载”机制和COW机制有了一定的认识,本篇文章我们将再次深化理解,并对相关内容进行拓展

目录

一、虚拟地址空间到底是什么?

1.上篇虚拟地址空间概念总结

2.大富翁的例子

3.结合例子深化理解进程与虚拟地址空间的关系

Ⅰ.隔离与独立性

Ⅱ.“承诺”≠“真实拥有”

Ⅲ.操作系统的 “管理” 角色

Ⅳ.“大饼” 的欺骗性

4.mm_struct结构体

二、如何理解虚拟地址中的区域划分

三、深入了解页表

1.标志位

Present(存在位)

Read/Write(读写位)

Execute(执行位)

2.为什么全局变量的生命周期是全局的?

3.常量字符串为什么是只读的?


一、虚拟地址空间到底是什么?

1.上篇虚拟地址空间概念总结

回顾上篇文章,我们已经知道了我们所涉及到是栈内存空间、堆内存空间、数据段等等,包括C语言的指针指向的地址,通通都是虚拟地址空间,我们是无法访问到真实的物理地址空间的,这是OS的一个强力的权限限制,这么做是为了保护我们的内存。

2.大富翁的例子

这里讲一个小故事帮助大家理解虚拟地址空间:

有一个海外大富翁,拥有10个亿资产,同时有三个私生子,每一个私生子都以为自己是独生子,对其他人的存在并不知情。

有一天大富翁对私生子1说,你好好工作,专注于事业,等我百年之后,我的十个亿都是你的,私生子1很开心;大富翁又找到私生子2说,你大学好好钻研金融专业,将来替我管理公司,等我死后,十个亿都是你的,私生子2狂喜;后来大富翁又找到私生子3说,你高中努力学习,等我去世后我的十个亿资产都给你,你想做什么就做什么,私生子3开始埋头苦干。大富翁跟这几个私生子说的承诺本质上是画一个大大的饼。

但是他们真的能一次要到十个亿吗?当然不可能,运气不好的,能要到千把块钱,运气好的,能要到一万十万,因为老子还在世呢!每一个人要的时候,只会一点一点地要,但是他们都沉浸在自己能有十个亿的幻想当中。换到大富翁的角度,他给那么多人画饼,万一忘记怎么办?那需不需要把这些“饼”给管理起来?当然需要!

在这个故事当中,这些“饼”就是虚拟地址空间!!!而大富翁就是操作系统OS,那十个亿就是真实的物理内存大小,这一个个私生子都是一个个进程,当它们需要内存的时候,操作系统就会给它们分配虚拟内存空间

有意思的是,这里面的进程都以为自己独占了全部内存资源,其实也只是得到了一个承诺(虚拟地址空间),进程拿到的虚拟地址,和真实的物理地址不是一回事,就像私生子拿到的是 “未来能拿到钱的承诺”,而不是直接拿到现金。操作系统会通过页表,把进程使用的虚拟地址,映射到真实的物理内存地址上。当物理内存不够用时,还会把暂时不用的内存数据放到磁盘上(交换分区 / 分页),就像大富翁暂时拿不出钱,先写个欠条,等你真要用了再想办法凑钱。

3.结合例子深化理解进程与虚拟地址空间的关系

Ⅰ.隔离与独立性

每个私生子(进程)都不知道其他人的存在,以为自己是独生子,能独享 10 个亿。对应到虚拟内存,每个进程都有自己独立的虚拟地址空间,看不到也碰不到其他进程的地址,天然实现了进程间的内存隔离,防止互相干扰和破坏。

Ⅱ.“承诺”≠“真实拥有”

私生子(进程)拿到的只是 “未来能拿到 10 个亿” 的承诺(虚拟地址空间),而不是立刻拥有所有钱。只有当进程真正需要用内存时,操作系统才会把物理内存分配给它,这就是按需分配的核心思想,避免了资源浪费。

Ⅲ.操作系统的 “管理” 角色

大富翁(OS)需要管理好给每个私生子画的饼,不能让他们的需求冲突。对应到技术上,就是操作系统通过页表、缺页中断等机制,把虚拟地址转换成真实的物理地址,同时管理物理内存的分配、回收和换入换出。

Ⅳ.“大饼” 的欺骗性

每个进程的虚拟地址空间大小,通常都远大于物理内存(比如 32 位系统每个进程有 4GB 虚拟地址空间,而物理内存可能只有 2GB),就像大富翁的 “10 个亿” 被分给了好几个私生子,但真实资产只有一份。这正是虚拟内存解决 “物理内存不足” 问题的关键 —— 用虚拟地址空间的 “假象”,让多个进程高效复用有限的物理内存。

4.mm_struct结构体

说了这么多,那虚拟地址空间到底以什么样的形式存在?它本质就是进程数据结构当中的一个结构体而已!!!那这个结构体应该包含什么呢?如下

这里面包含了虚拟空间的内存总数、栈空间的起始位置与结束位置、堆空间的起始位置和结束位置、代码段的起始位置和结束位置等等。

先通过mm_struct里的字段,描述清楚进程虚拟地址空间里每一段(代码、数据、堆、栈)的位置、大小、属性;再通过这些信息,组织起页表映射、内存分配、回收、缺页中断处理等所有和虚拟内存相关的操作。本质上还是先描述再组织的道理

二、如何理解虚拟地址中的区域划分

如下是上篇讲解的空间分布图的一部分,可以看到,栈和堆是相向增长的

我们以栈和堆的空间来讲解虚拟地址中的区域划分

从上面提到的mm_struct结构体当中,有明确区分栈和堆的起始位置和结束位置的代码,本质上就是把虚拟地址中的区域划分了,区域内,相当于获得了一批有效地址!!!区域内的任何位置,对应的栈或者堆都可以直接使用。

当然也会存在栈或者堆的虚拟地址空间使用超过原定范围的情况,此时OS就会自动调整区域,比如给栈多点空间或者给堆多点空间来动态调整并解决这个问题,当然,栈和堆的动态调整扩展机制是不一样的,这里先不详细介绍他们的具体机制

三、深入了解页表

上篇我们知道了页表支持了程序的虚拟地址与真实物理地址的哈希映射关系,但是除了虚拟地址与物理地址映射之外,还有一个我们要讲的地方,用来表示数据的各个状态的,就是标志位!

1.标志位

常见的标志位有:Present(存在位)、Read/Write(读写位)、Execute(执行位)等等,我们一一来简单介绍一下:

Present(存在位)

Present为1:表示该虚拟页已映射到物理内存,页表项中的物理地址有效;Present 为0:表示该虚拟页不在物理内存中(可能被换出到交换分区,或还未按需加载),此时访问会触发缺页异常(Page Fault),操作系统会处理:若页在交换分区,就把它换回到物理内存;若页是首次访问(如代码段、未初始化数据段),就从磁盘文件加载进内存。Present为0时,页表项的物理地址字段通常会存 “该页在交换分区的位置”,而不是物理内存地址。

Read/Write(读写位)

用于控制页的读写权限:0 = 只读,1 = 可读写,是为了保护代码段,还会用于保护只读数据段、共享库的只读部分,防止进程自身或其他进程非法修改

Execute(执行位)

表示控制页是否可执行,主要实现数据段不可执行(防溢出攻击)、代码段只读。

一般与读写位混合使用:

代码段:Read/Write=0(只读) +Execute = 1(可执行)

数据段:Read/Write=1(可读写) +Execute = 0(不可执行)

2.为什么全局变量的生命周期是全局的?

简单来说全局变量的生命周期 = 进程的生命周期,物理地址的存在是其能够被访问的必要条件,但生命周期的起点是程序启动,终点是程序结束,由进程的本身存在性决定。这也就解释了为什么进程重启后,全局变量会被重新初始化了,因为新的进程会重新加载数据段,分配新的物理地址。

3.常量字符串为什么是只读的?

我们都知道添加const修饰的变量都无法被修改,要么修改后程序崩溃,要么是编译器直接报错,这里就要分两种情况讨论了:

Ⅰ.首先是全局的const修饰的变量,编译器会把这个变量放到.rodata(只读数据段)中。操作系统在为.rodata段建立页表映射时,会把页表项的Write位设为0(只读),当你试图修改它时(比如通过指针强写 *(int*)&a = 20),CPU 会检测到写操作访问了只读页,触发写保护异常(Segmentation Fault),直接终止进程。本质上是页表的写权限为 0,硬件直接阻止了修改,修改全局const变量,会触发保护异常,程序直接被抹杀,这也就是为什么编译器这样执行代码会崩溃的原因。不是编译器让程序崩溃,是页表权限 + CPU 硬件保护 + 操作系统三者联手把程序 “抹杀” 了!

Ⅱ.第二种是局部的const修饰的变量,它仅仅编译器层面的 “不可修改”,这个变量分配在上,而栈所在的页表项Write位是1(可读写)的。它的 “不可修改” 只是C 语言编译器的语法检查:编译器会阻止你直接写b = 30,但如果你用指针绕开编译器检查(比如 *(int*)&b = 30),修改是会成功的,不会触发错误。因为它的页表写权限是开着的,没有硬件保护,只是编译器不让你改而已。

本篇到此结束,下篇我们将继续讲解相关的内容

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/27 13:05:46

协程框架高并发翻车了?三个C++ Web框架实测,结果出乎意料

上一版测完总觉得哪里不对,于是改了配置又跑了一遍——这次结果老实多了。 前言:为什么重新测了一遍 这篇文章其实是第二版了。上一篇发出来之后我越想越不对劲——当时每个容器只给了 512MB 内存,fd 上限也没显式调高,跑 10K 并…

作者头像 李华
网站建设 2026/5/27 13:05:01

AntiDupl终极指南:快速清理重复图片的智能解决方案

AntiDupl终极指南:快速清理重复图片的智能解决方案 【免费下载链接】AntiDupl A program to search similar and defect pictures on the disk 项目地址: https://gitcode.com/gh_mirrors/an/AntiDupl 如果你正在为电脑中堆积如山的重复照片而烦恼&#xff0…

作者头像 李华
网站建设 2026/5/27 13:05:00

Ryujinx模拟器入门指南:5步轻松玩转Switch游戏

Ryujinx模拟器入门指南:5步轻松玩转Switch游戏 【免费下载链接】Ryujinx 用 C# 编写的实验性 Nintendo Switch 模拟器 项目地址: https://gitcode.com/GitHub_Trending/ry/Ryujinx Ryujinx是一款用C#编写的实验性Nintendo Switch模拟器,让PC玩家能…

作者头像 李华
网站建设 2026/5/27 13:02:33

电脑怎么删除重复文件?重复文件清理详细教程(纯干货)

在日常使用电脑的过程中,很多人都会遇到这样一个问题:明明没有刻意保存多份,电脑里却出现了大量内容相同的文件。它们分散在不同的文件夹中,不仅占用宝贵的存储空间,还让文件管理变得混乱不堪。那么,电脑怎…

作者头像 李华
网站建设 2026/5/27 12:58:32

2025年专访AI短剧平台盈利实操心得

2025年AI短剧赛道热度持续走高,从内容生产到流量变现的全链路智能化已经成为行业共识,本次我们专访了东营优腾智能科技有限责任公司相关负责人,围绕AI短剧平台的实操盈利经验展开对话。记者问:现在市面上的AI短剧软件品类繁杂&…

作者头像 李华
网站建设 2026/5/27 12:58:00

炉石传说终极模改插件:如何用HsMod彻底改变你的游戏体验

炉石传说终极模改插件:如何用HsMod彻底改变你的游戏体验 【免费下载链接】HsMod Hearthstone Modification Based on BepInEx 项目地址: https://gitcode.com/GitHub_Trending/hs/HsMod 炉石传说玩家们,你是否厌倦了漫长的等待动画?是…

作者头像 李华