Hello! 欢迎来到小浪云!


Linux系统编程:进程地址空间


Linux系统编程:进程地址空间一,内存地址空间1.1,区存储变量:普通局部变量指针变量、函数参数、函数返回地址、临时变量、寄存器变量;

函数参数:函数的参数是从右到左依次入的;

Linux系统编程:进程地址空间在vs2022上栈区并不是”向下生长的”,而是正常的栈,因此推断结果应该是b的地址较低,a的地址比b的地址高;

Linux系统编程:进程地址空间函数返回地址:函数返回地址是指函数执行完毕后,程序需要跳转回继续执行的内存地址(即函数的地址)。当一个函数被调用时,当前程序的执行流程会转移到该函数内部。在函数执行完成后,程序需要知道回到哪里继续执行后续的代码,这个“回去的位置”对应的内存地址就是函数返回地址。

1.2,区介绍

特点

动态分配:程序运行时可根据需要随时在区申请和释放内存空间,大小可在运行时确定,适应不同数据结构算法的需求;

空间较大:一般来说,堆区的空间比栈区的空间更大,因为它是随机开辟的,并不像栈区地址都是连续的,所以空间更大,方便使用;

管理复杂:需要程序员手动管理内存分配和释放,容易引发悬空指针和由于忘了释放内存造成的内存泄漏问题;

1.3,全局区(数据段)与常量区全局区也称为静态存储区,用于存放全局变量和静态变量;

生命周期:随着进程结束释放内存;即便是函数内部的局部静态变量生命周期依旧是整个进程;

Linux系统编程:进程地址空间我们可以看到即便是超出了作用域Static int a 的地址空间依旧没有释放;这说明静态局部变量的生命周期不是随作用域的,而是随进程的;但只能在作用域中使用(在不返回值的情况下); ——>静态局部变量相当于全局变量,唯一的区别就是作用域不同;

1.4,代码段数据段是进程中存储执行代码的内存区域,它包含了程序的指令和常量(“1”,”hello world”….);

二,fork的遗留问题为什么fork有两个返回值?

这其实是在两个进程中看,父进程执行到fork时还没来得及return,此时子进程就已经创建出来了,并且也会return一个值,所以有两个值;那这么看就会产生两个数据,对吧;接下来我们验证一下他们的地址;

Linux系统编程:进程地址空间Linux系统编程:进程地址空间我们可以看到id的值是不一样的,但地址确实一样的,这是为什么呢?按道理说,如果地址相同那同一个地址怎么可能存两个数据呢?况且还发生了写时拷贝,地址怎么会相同呢?

其实这里我们看到的是虚拟内存地址;

先来解释一下上面的情况:

—->父进程创建子进程时确实发生了写时拷贝,id变量的确有两份,不过是写时拷贝开辟的空间是物理内存,这个时候物理内存上存在两个id地址,一个是父进程的,一个是子进程的,而我们在程序中看到的是虚拟内存地址;物理内存无法在程序中看到;

问题一:为什么要有虚拟地址(进程地址空间)

1.统一进程视角看待内存

现在我们使用OS类比一个大富翁,3个进程类比3个他的孩子;一个大富翁(操作系统)有10亿美金,而他有四个私生子,但是四个私生子(进程)都并不知道对方的存在(进程独立性),所以他们都认为大富翁只有他唯一一个儿子,而大富翁告诉他们一旦自己去世了,就把所有的家产留给他,所以每个儿子也都信了,因此大富翁其实给每个私生子都画了一个大饼(进程地址空间)。每个人都认为自己有十亿家产。但实际上是这些私生子要多少才会给多少(进程需要多少空间操作系统就给多少空间

如果有一张虚拟内存,这样每个进程就不需要关心当前的物理内存会不会影响到别的进程,我用的时候直接告诉OS,然后他帮我们分配,这样可以更加方便有序的使进程运行;

2.保护内存地址,出现问题直接拦截(相当于加了一层防护)

当我们申请物理内存空间时,就会利用虚拟地址进行地址审查,在这个转化过程中,如果虚拟内存地址出现问题,就会直接结束这个过程,就不会直接影响到物理内存;

为什么我们无法修改常量字符串?

常量字符串位于常量区域,但仅仅如此不足以说明不可修改,我们都知道不可修改是一种权限,那这个权限在哪里呢?其实这个权限是在页表中,当通过虚拟地址访问物理地址时,会通过页表转化并检查权限,如果没有权限就会被拦截;

三,什么是进程地址空间我们知道要管理一个对象的方法是–>先描述再组织;

Linux系统编程:进程地址空间四,页表现代操作系统不做浪费时间和空间的事;

4.1,写时拷贝,缺页中断,惰性加载首先,页表中有什么呢?

Linux系统编程:进程地址空间答:虚拟地址、物理地址、权限位、标志位(是否将对应的代码和数据加载到内存中)

权限位有什么用?

权限位上有该地址的读和写权限,如果该地址是只读权限那么我们对地址进行修改就会被OS直接拦截,非法请求就不会发送到物理内存,对物理内存起到一定程度的保护作用;

标志位是什么?

标志位是检查进程该地址需要的代码是否加载到了内存中;

惰性加载:就是需要多少加载多少,操作系统对于大文件是可以实现分批加载的,也就是进程可能有时会只有PCB在内存中;

缺页中断:当所需的代码和数据还没有被加载进内存的时候,这时候就会发生缺页中断,中断的意思就是暂时暂停此进程,等待代码和数据加载进来后就会继续执行进程;

那么为什么不一次性将代码数据加载到内存中呢?

从空间的角度思考,一个大文件加载是需要占用很大的空间的,而且进程一开始也不会马上用到一整块代码,所以这个时候有一些代码数据是空闲的,也就占用了额外的内存空间;

从时间的角度考虑,加载一个大文件十分耗费时间;一次性加载效率不高;

因此出现了缺页中断,其用意就是对代码和数据进行局部性加载,合理使用内存从而提高效率;

写时拷贝:数据段(全局区)的数据本来是可写的,但权限确实只读的,这么做的目的就是为了维持写时拷贝;当进程双方中的一方对数据进行修改,就会触发写时拷贝机制,重新开辟一块空间,存储新的数据,并且修改页表映射;

4.2进程地址是如何被切换的Linux系统编程:进程地址空间进程PCB结构体里有对应的进程地址空间指针,所以进程切换就意味着进程空间地址空间被切换,而页表会被存储在CPU的cr3寄存器中,这其实属于进程的上下文信息,在进程切换的时候会被进程带走,后面再恢复过来!

4.3进程创建的具体分析过程进程被创建的时候,优先被创建和加载的是PCB数据结构和对应的地址空间,代码和数据等到需要的会后在加载进来;

4.4重新理解进程具有独立性1.在PCB数据结构上,每一个进程都有自己唯一的PCB;

2.虚拟地址可以相同,但通过页表映射的物理地址是不同的;各自有各自的区域,对于父子进程在写时拷贝的机制下,也是拥有自己独一份的物理地址;

五,命令行参数和环境变量在栈的上面Linux系统编程:进程地址空间Linux系统编程:进程地址空间

相关阅读