程序地址空间

地址空间

地址空间

在这里插入图片描述
我们之前学习c语言基本都是学习这样的空间布局,但是事实上并不是这样

写时拷贝

我们先写一段代码来看看

在这里插入图片描述

在这里插入图片描述

现在,这个结果大家都认识,我们之前也写过,我们再改一下代码
在这里插入图片描述
我们只让子进程中写上了value++,看会出现什么情况
在这里插入图片描述
我们可以看到,我们的value明明是全局变量,子进程和父进程都就可以访问到,子进程修改了,但是不影响父进程,这是我们之前说过的写时拷贝,我们也可以说进程具有独立性

现在我们来聊聊写时拷贝,我们看到,value的值虽然不一样,但是地址是一样的,我们之前也说过这个是虚拟地址(线性地址),不是物理地址,实际上物理地址是不一样的

讲写实拷贝之前,我们得先有一些储备知识:

进程地址空间也是一个结构体struct mm_struct我们访问进程的数据的时候就是要先访问进程地址空间(虚拟地址),然后在找到物理地址
那么我们说的数据区,堆区,栈区怎么理解?

地址空间就是一个线性范围,从1到4294967295,线性排列,1个地址代表一个字节,当我们需要一个int类型的变量,就会开辟4个地址(字节),并且返回其低地址,而我们怎么知道它为这个变量开辟了多少地址呢?我们是靠类型知道的,这就是类型的意义 - - - 地址空间是线性的

这些区域每一个都是由两个指针一个是start指向区域的开始,end指向区域的结束位置,这样划分出的一块区域,限定了区域之后,这些区域之间的一个个数据就是虚拟地址

我们的进程的pcb的虚拟地址在地址空间中,我们需要pcb中的数据的时候,就会在页表中找到虚拟地址实际的物理地址,去访问其中的数据,页表就是一个kv结构,存了虚拟地址和物理地址

子进程的pcb是按照父进程来创建的,内容大部分都相同,子进程的mm_struct和父进程的mm_struct相同,都是指向同一个虚拟地址,然后找到同一个物理地址,所以子进程和父进程的数据会一样,然后我们想在子进程中修改数据,这时候操作系统会判定不能修改,所以会给子进程找另一块物理地址给子进程,让它去修改,而没有改变子进程的虚拟地址,就是子进程的页表和父进程的页表储存的kv是一样的,子进程改了物理地址,就是相当于改了k或v,而我们看到的虚拟地址就是没有被改那个k或v

现在我们可以解释一下我们经常写的fork之后if和else if同时执行的原因了

因为id是我们定义的一个局部变量,在父进程中,所以我们在fork函数里面,创建了好了子进程,然后子进程和父进程都会执行一次fork函数的return,返回给id,而先return的那个就会发生写时拷贝,将物理地址改了,虚拟地址不变,所以我们才会看到id有两个值,会执行if和else if,就是父进程通过它的id去判段执行力if和else if其中一个,子进程通过它的id判断执行了另一个

写时拷贝本质上是一个按需申请的策略,子进程和父进程的内容基本完全跟一样,只有当我们需要修改子进程的数据的时候,才会写时拷贝,该改变物理地址

为什么要有虚拟地址,要在页表映射一下才能访问物理地址?

  1. 防止地址随意访问,保护物理内存和其他进程

我们以前是直接访问物理地址,没有虚拟地址的,所以我们写的代码可能有越界,修改了不属于我们的区域,这样可能两个地方都崩溃了,又或者是有人故意写的一个越界访问的代码,这样去读取别人的信息数据,这样别人的信息就泄露了,所以加一个虚拟地址,页表来映射就可以让操作系统来判断你是否越界,如果你越界直接不让你访问,只会你的代码崩溃,不影响其他地方

malloc的本质(缺页中断)
我们在malloc的时候其实不是申请了内存就操作系统立马给你分配物理内存,因为操作系统是不允许任何的浪费和不高效行为,所以操作系统会给你分配虚拟地址,也就是说页表就写了一半,另一半空着。等到你真的要访问你malloc的空间的时候,操作系统才会给你分配物理空间,再让你访问

  1. 解耦合
    我们有了地址空间和页表,我们进程就只关心自己这块地址空间,物理地址到底在哪里不重要,操作系统会分配,这是进程管理,物理内存管理,就只需要处理好内存分配,怎样分配最大化利用内存等等,这就做到了进程管理和内存管理解耦合
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值