Linux中的进程地址空间

进程空间

温故学习c语言的时候


我们在学习c语言的时候一定是看到过这张图的
在这里插入图片描述

当时我们的理解是认为这张图描述的是内存空间, 但其实不是的。

我们先来验证一下这张图

在这里插入图片描述
我们用这个代码来打印出上图所对应的地址

在这里插入图片描述

可以看到确实地址是从低到高的

在这里插入图片描述
在开辟空间是, 栈区是自上向下开辟(大地址到小地址) 而堆区是自下向上开辟的(小地址到大地址)

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

可以看到栈的地址是从大到小的

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

可以看到堆区的地址是从小到大的

我们来验证一个语法问题

我们知道当我们给变量定义static时, 该变量不会随着函数的释放而被释放
在这里插入图片描述
为什么呢?

我们打印一下他的地址
在这里插入图片描述

在这里插入图片描述
可以看到他们之间的地址差距非常大了, 而他的地址与在全局区定义的变量差别不大;

这说明: 我们用static修饰的变量, 编译的时候已经被编到全局数据区去了, 所以他不会随着函数的释放而被释放了。

在这里插入图片描述
我们运行这段代码

在这里插入图片描述
发现当子进程改了g_val的值后子进程的值变了, 但是父进程的没变;

这也好理解, 因为进程之间具有独立性, 当子进程在更改的时候会发生写实拷贝。 但是, 父子进程指向的变量是同一个地址的, 这是为什么呢?

为什么他们在同一个地址上却读到了不用的值???

以我们现在的知识还无法理解这种情况, 但是我们可以下一个结论

如果上述的地址是物理地址, 那么绝对是不会出现这种情况的, 所以 上述的地址绝对不是物理地址 那这个地址不是物理地址是什么呢? – 线性地址/虚拟地址

同样, 我们平时在使用c/c++指针之类的时候, 所用到的地址, 全部都不是物理地址

至于这个地址是哪来的, 有什么用, 我们到后面再说。


引入新概念, 初步理解这种现象 – 映入地址空间的概念

进程地址空间

当我们实际在运行一个程序是, 他会变成一个进程, 操作系统一定会为这个进程开辟一个pcb, pcb会指向代码和数据, 我们称pcb以及他所指向的代码与数据称为进程, 但实际上没这么简单。

当我们在创建出pcb后, 为了让该进程更好的运行,操作系统除了创建出pcb以外, 还要为该进程创建一个叫地址空间的东西也就是进程地址空间, 这个东西也就是我们在学c语言时的那张图

i在这里插入图片描述

页表

我们先简化页表的信息, 不谈他过多的功能

在这里插入图片描述
页表负责存储地址空间中的虚拟地址, 和其对应的物理地址, 当我们需要对某个变量进行更改时, 就会通过页表找到他的物理地址, 然后在对其进行修改。

当我们在创建紫子进程的时候, 子进程会以父进程为模版, 创建出自己的pcb由于每个进程都有自己的进程地址空间, 所以子进程也会通过父进程拷贝一份地址空间,同理, 子进程也有自己的页表

在这里插入图片描述

初步解释之前遇到的问题

为什么同一个地址上读取到的数是不同的?

因为,我们实际上查看到的是虚拟地址, 当子进程修改数值后, 发生写实拷贝时, 虚拟地址是不会变的, 改变的是其通过页表所指示的物理地址, 所以我们才看到了, 一个地址有两个值的假象

谈论一些细节

地址空间究竟是什么?

什么叫做地址空间?

地址总线排列组合形成的地址范围(0 - 2 ^ 32)

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

在这里插入图片描述

所以什么是地址空间呢?

所谓的进程地址空间, 本质是一个描述进程可视范围的大小,
地址空间为一定要存在各种区域划分,对线性地址进行star和end即可;

地址空间本质是内核的一个数据结构对象, 类似于pcb, 地址空间也是要被操作系统管理的:先描述再组织
在这里插入图片描述
默认划分的区域为4GB

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

页表

页表的作用

在这里插入图片描述
记录虚拟地址所对应的物理地址

cr3寄存器

在这里插入图片描述
cr3寄存器里存储的是该进程的页表的物理地址, 之前我们提到过,当进程被换下后, pcb会将带走cpu上的信息, 这些信息中就包括这个。

页表是怎么知道我当前申请访问的地址是只读的或者是其他的呢?

在这里插入图片描述
页表中有一个标志位, 能记录当前地址的权限是什么。

如果你要进行非法操作, 页表能直接拦截你的行为, 所以页表也是可以提供权限管理
这也就是为什么字符常量是不能被修改的。

解决之前的问题

我们之前学进程状态的时候, 学了 S R T之类的状态, 但是好像有一个状态没提到, 没错, 就是挂起状态, 我们知道, 当进程处于阻塞时吗且内存资源严重不足的时候, 操作系统会先移走该进程的代码和数据(不是pcb也不是进程空间也不是页表) 以腾出空间。

我怎么知道我的进程是否被挂起了呢?

共识现代操作系统,几乎不会做仍和浪费空间的和浪费时间的事情

我们现在稍微大点的游戏就是好几十个G 然而我们电脑的内存也就才16个G左右, 却可以正常运行这些游戏, 这其实就说明了, 我们的操作系统可以实现对大文件的分批加载。

补充

页表中还有一个标志位, 用于标注该地址所对应的代码是否被加载到了内存中
在这里插入图片描述

我们的操作系统采用的是惰性加载的方式 先将一部分代码和数据加载到内春中去, 当操作系统访问某个地方的时候发现在页表里找不到他对应的物理地址此时就会触发缺页中断然后待改地址的数据和代码加载到内存后在进行操作。

这里我们就可以进一步理解一下之前我们学到的写实拷贝了

进一步理解写实拷贝

实现写实拷贝的原理其实就是缺页中断 当我们在想修改数据区时, 操作系统就会识别到数据区本来是可以写的, 但是却被页表设置为了只读, 于是开辟一个新空间, 然后再在这个新空间写入数据, 最后将地址填入页表中, 这就是写实拷贝的过程

Linux 的内存管理模块

我们前面提到了那么多的申请地址, 比如发生缺页中断时系统会新申请一段空间来加载对应的代码和数据, 但是物理地址那么大, 我们到底是在哪里申请的呢?申请下的内存又是怎么被填写会页表里的? 我们一次加载多少代码和数据, 加载到哪里呢? 等等等等, 这些都是由操作系统来做的
而这些统称为Linux的内存管理模块

到这里我们就可以理解了 为什么要有进程地址空间
在这里插入图片描述

什么叫做进程?

进程具有独立性, 怎么做到的?

1 每个进程有自己的pcb 自己的进程地址空间, 自己的页表, 他们在内核数据结构上是独立的,。
2 通过页表的进程之间的数据也是互相独立的, 即使父子进程的代码时一样的, 但是通过页表, 他们所对应的数据也是相互独立的。

  • 17
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值