我们思考之前没有解答的一个问题的一个问题
就是为什么id可以是两个值???
https://blog.csdn.net/Starry_tsx/article/details/152663966?spm=1001.2014.3001.5501

运行结果是这样的

我们发现两个id同一个地址 并且值还不同
所以我们看到的这个地址肯定不是物理地址
那么这个地方是什么地址???
只可能是虚拟地址
那么实际物理地址呢
虚拟地址和物理地址有什么联系呢
虚拟地址和物理地址在页表中产生联系
页表存在于内存中
页表我们可以类似理解成一种映射关系
将我们的虚拟地址映射到物理地址上
有什么好处 或者说为什么呢
1.地址空间隔离:每个进程拥有独立的页表,对应专属的虚拟地址空间。
进程只能通过页表访问自己的虚拟地址,无法直接越界访问其他进程或内核的物理内存,实现了 "进程间内存互不干扰"。
2.内存访问保护:页表可设置权限标志(如 "只读""可写""执行禁止"),防止进程非法修改内存(如越权写其他进程的只读段),增强系统稳定性与抗攻击能力。
3.程序基于 "连续的虚拟地址空间" 编写,无需关心物理内存的实际分布(是否连续、物理地址具体值)。
编译时仅生成虚拟地址,运行时由操作系统通过页表动态映射物理地址,实现 "一次编译,多平台运行",降低了硬件差异对程序开发的影响。
4. 支持 "内存共享与复用",节省系统资源 多个进程可通过页表共享同一物理页帧(典型场景:共享库、进程间共享内存)。例如,多个进程使用 C 标准库时,只需将库的物理页帧映射到各进程的虚拟地址空间,无需在物理内存中加载多份副本,大幅节省内存占用。
那么 我们前面讲过子进程的写实拷贝
原本父进程和子进程公用一份数据
也共用一份页表
但是当我们对子进程数据进行操作时
为了保证进程的独立
我们会开空间把父进程的数据单独拷贝做修改
同时对子进程的页表拷贝但是虚拟地址不变
虚拟地址映射的物理地址变成了子进程开空间的地址
这也是为什么上面那个代码运行出来两个id的值不同但是地址相同
它们只是虚拟地址相同 物理地址不同 所以其值也不同
包括我们之前看到的很经典的一张图也是虚拟内存的图

那么进程如果这个进程重新排队后怎么找到页表呢???
有一个cr3的寄存器 存放着页表的地址
这个属于进程的硬件上下文 进程结束其数据会打包带走的
当这个进程再跑的时候 这个进程的页表地址覆盖掉cr3的地址
这样我们就可以找到页表了
接着再思考一个问题
我们知道虚拟内存地址有栈区 代码区等等 但是比如常量区是不能被修改的
页表是咋处理的?
页表会对不同的区域有其权限字段 比如说字符常量区 页表描述权限的字段就没有写的访问权限
当一个进程以写权限访问 这便是非法访问 操作系统就有可能会杀死这个进程