进程地址空间

重点:不要只记住结论,而是要整理得出结论的逻辑10-19
不要长篇大论,要写有逻辑的东西,整理出链路来

内存寻址的最基本单位是字节

C/C++时期程序员看待内存的地址空间

在这里插入图片描述

对子进程的全局和父进程的全局变量进行了修改,输出这个变量的地址,父子进程都是同一个地址,但是他们的内容值就是不一样,这样就绝不可能是物理地址,只能是虚拟地址。
经过代码实验可以得出结论,你们平时写的C/C++用的指针,指针里面的地址,全部都不是物理地址!!!
在这里插入图片描述

在这里插入图片描述

引入进程地址空间的概念

注意每一个进程都要有一个进程地址空间的东西,子进程要把父进程的进程地址空间拷贝一份!
同时也拷贝了PCB结构体,虽然很多东西都是从父进程拷贝过来的。

对待页表同样也要拷贝一份,但是物理内存只有一份
此时子进程和父进程就做到了共享代码和数据。
在这里插入图片描述

当对 g_val这个全局变量进行修改的时候(上面实验的代码),会发生写时拷贝,操作系统通过子进程页表的虚拟地址找到物理地址发现这是和父进程共享的数据,此时操作系统先不让你拷贝,它去物理内存中开辟一块新空间后且修改了页表中g_val的物理地址,再让你对这个新的物理地址进行写入。
通过相同的虚拟地址映射到不同的物理地址,所以才有了我们上面实验的现象(同一个地址但是打印出的值不同)
在这里插入图片描述
对g_val的写时拷贝 是由操作系统自动完成的。
在这里插入图片描述

解决历史遗留问题

为什么同一个id变量既等于0又大于0?
在这里插入图片描述
fork()的返回值本质是对id进行了写入,又因为fork后子进程已经被创建出来了
他们通过页表找到同一个虚拟地址映射到了不同的物理地址处!

什么是进程地址空间?

一定是进程在cpu运行,才有进程访问内存,有页表和地址空间这些东西
cpu通过地址总线找到进程要访问哪一个个地址

32位计算机中,有32位地址和数据总线
每一根总线只有0,1,32根就是2^32种
根据内存寻址基本单位字节那就是2^32 byte = 4GB

地址空间就是 地址总线排列组合从0000 0000 ->FFFF FFFF 形成的地址范围[0,2^32]

如何理解地址空间上的区域划分?

假设有一张桌子一共100cm,两个人用一张桌子各自别越界
用计算机语言如何解释?本质也是结构体描述起来,区域划分就是在结构体内定义他空间的start 和 end 变量。
在这里插入图片描述

所以,什么是地址空间呢?

地址空间本质是描述进程可视范围的大小
地址空间内一定要存在各种区域划分,对线性地址进行start,和end即可
地址空间本质是内核的一个数据结构对象struct mm_struct,类似PCB一样,地址空间也是要被操作系统管理的:先描述,在组织
在这里插入图片描述
有了区域划分,如果我们用main函数的地址进行写入,这个地址落在了代码区,那操作系统就识别出了越界就不让你访问了

为什么要有进程地址空间?

原因有三
在这里插入图片描述
如果没有地址空间,直接让进程直接访问物理内存,PCB就要记录在物理内存的开始地址和结束,PCB还要对物理内存中代码和数据进行管理工作,每个进程都要做,太麻烦了。
有了地址空间进程根本就不关心物理内存是什么样的,反正我要内存就申请就可以了
1、有了地址空间会让进程以统一的视角看待内存
当我们有了地址空间和页表,把代码和数据加载到物理内存时理论上可以放到物理内存的任意地方,因为有页表呢,页表会把无序内存映射为有序。
物理内存可以不连续,哪怕是把大程序划分多个小块,也可以在物理内存上无序加载,对于进程来说,你要找代码就去划分好的代码区去找,要去找栈上的数据就去栈区去找,所有的相关数据都在一块,这样就很规整。进程要申请内存,物理内存有了页表映射自己重新建立映射,进程不用管物理内存是怎样的,它该申请内存申请,释放就释放。
这就解放了双方,实现了对进程管理模块和内存管理模块进行解耦!
2、因为有地址空间和页表的存在,将进程管理模块,和内存管理模块进行解耦合!
3、系统级的越界检查
增加进程虚拟地址空间可以让我们访问内存的时候,增加一个转换的过程,在这个转化的过程中,可以对我们的寻址请求进行审查,所以一旦异常访问,直接拦截,该请求不会到达物理内存,保护物理内存

地址空间怎么做到的?

页表

页表类似于map映射结构

一个进程要访问自己的内存,一定是进程正在cpu上运行的。
在这里插入图片描述

1、如何找到页表?
通过cr3寄存器保存当前正在运行进程的页表起始地址(物理地址)。

2、进程切换 担不担心进程页表找不到呢?
不担心,进程会保存进程的上下文(cr3寄存器也属于上下文),切换后会恢复

3、页表权限标志位
C语言期间你说char* str = “hello bit” 常量字符串是只读的,无法修改的,为什么?
物理内存没有权限的概念,想写就写
因为页表有了读写标志位就控制了读写权限。

你怎么知道你的进程的代码和数据,在不在内存呢?
4、对应的代码和数据是否已经被加载到内存标志位 - 延时加载
前提共识:
现代操作系统,几乎不做任何浪费空间和浪费时间的事情
我们说进程要运行要先把代码和数据加载到内存里,它真的全都加载了吗?全部加载但你可能只用了一部分,大部分没用就会导致内存使用率下降,所以它采用惰性加载,你用了我再给你加载,大文件还要分批加载。

缺页中断
页表处理时可以把虚拟地址都填上,物理地址什么也不填,页表还有一个对应的代码和数据是否已经加载到内存的标志位,当我们访问一个虚拟地址,如果先看标志位是1已经被加载就直接找到物理地址访问,如果发现代码和数据没有被加载到内存(标志位是0),操作系统触发缺页中断,从磁盘把剩下的代码和数据加载到开辟的物理内存中,然后把这个 物理地址填到对应的虚拟地址后面,再恢复到对应当时要访问的过程此时就可以访问数据了。
此时我们可实现边使用边加载
写时拷贝也是缺页中断实现的。

缺页中断提高了初次加载局部效率,并且提高了内存使用率,没用的就先不加载,但不会提高程序整体时间成本效率,就像100块钱一次花,分10次花没有区别。

所以,什么叫做进程?以及进程地址空间—>为什么? ?

进程=内核数据结构(task_struct && mm_struct &&页表)+程序的代码和数据
在这里插入图片描述进程切换
进程要切换 要切换PCB ,地址空间,页表
切换了PCB,PCB指向了对应的地址空间,地址空间也被切换了
进程上下文(包含cr3寄存器)只要一切换,页表自动切换
每个进程都有这样一整套的东西。

进程具有独立性!为什么??怎么做到的?? ?
1、每个进程都有自己的PCB,地址空间,页表。父子进程都有独立的内核数据结构
2、还体现在物理内存中加载的代码和数据,页表层面上不同的进程虚拟地址可以完全一样,但物理地址可以完全不一样,只需要让页表映射到不同的物理地址上,不同的进程就互相解耦了。
即便是父子进程,页表映射物理地址的代码区一样,数据区不一样,那么数据层面上也解耦了。

所以一个进程是否自己的PCB,地址空间,页表,并不影响别人。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值