【Linux】程序地址空间 (虚拟地址空间 区域划分:区域调整 进程地址空间和物理内存之间映射关系 理解进程地址空间)


程序地址空间

在这里插入图片描述

它不是内存,它是虚拟地址空间。

虚拟地址空间

我们先见见它的特点:
在这里插入图片描述

在这里插入图片描述

刚开始父进程pid是5294,子进程pid是5295,对应的value和value地址刚开始打都是100,但是当我们子进程对全局变量进行了修改,子进程变量是300,父进程变量是100,地址却是一样的。原因是物理地址不同。

多进程在读取同一个地址的时候,怎么可能出现不同的结果?

地址没变 -> 这里的地址绝对不是物理地址 -> 曾经我们学习的语言级别的地址(指针),不是对应的物理地址 -> 虚拟地址(线性地址)【逻辑地址】

解释:

感性理解虚拟地址空间:
a.进程它会认为自己是独占系统资源的(事实上并不是)

在这里插入图片描述

说白了进程地址空间就是操作系统给进程画的大饼。

在软件层面,如何“画饼”呢??画饼的本质就是在大脑中构建一个蓝图 — 数据结构对象 。
在这里插入图片描述
操作系统有500个进程 ,给每个进程创建虚拟地址空间,进程要被管理,虚拟地址空间也要被管理。
如何管理?先描述在组织。
地址空间本质:是内核的一种数据结构 mm_struct

地址空间有很多区域,那么它是内核数据结构,它的成员有哪些?

区域划分:区域调整

在这里插入图片描述

在这里插入图片描述

32位的数据 -> unsigned int (32bits)

当我们创建一个进程,操作系统给这个进程malloc一个task_struct内核数据结构。接着操作系统结合地址空间创建了一个mm变量。
在这里插入图片描述

heap && stack 所谓区域调整,本质就是修改各个区域的start or start

我们在C/C++种,定义局部变量,malloc new堆空间就是扩大栈区域或者堆区。在函数调用完毕或者free空间的时候就是缩小堆区或者栈区。

进程地址空间和物理内存之间映射关系

在这里插入图片描述

上面所有操作都是由操作系统帮我们做的。

进程1进程2是两个不同的进程,两者在物理内存中有各自的代码和数据,进程1 进程2看不到物理内存,只能通过自己的地址空间访问。

在这里插入图片描述

理解进程地址空间

为什么存在进程地址空间?

1.如果让进程直接访问物理内存,进程可能会越界非法操作。但是虚拟地址空间可以变相的保护,当进程在非法访问时页表就拦截了。因为有页表的存在,所有的进程都会映射到合法的内存中

2.地址空间的存在,可以更方便的进行进程和进程的数据代码的解耦,保证了进程独立这样的特性
如下如,两个进程和其进程地址空间,中间是物理内存。

在这里插入图片描述

修改过程和左右两侧虚拟地址没有关系,所以上层在用的时候用的是虚拟地址,底层经过页表被映射到内存中不同的区域,此时我们看到的现象是大家的地址一样但内容不一样。
写时拷贝:当写入的时候就重新拷贝一份,这个工作是操作系统自动做的。
操作系统通过地址空间,通过页表,让不同的进程,映射到不同的物理内存处。

3.让进程以统一的视角,来看待进程对应的代码和数据等各个区域,方便使用编译器也以统一的视角来进行编译代码,编完即可立即直接使用

重新再理解一次地址空间:

在这里插入图片描述

进程的task_struct由指针指向内核当中的数据结构指向mm_struct,经过地址空间经过页表映射到物理内存。

假设磁盘有一个我们写的程序,名字叫my.exe,编好了代码对应的可执行程序,在没有被加载到内存的时候我们的可执行程序里面早就有地址(程序内部使用的地址)了,这个地址叫做逻辑地址。编译器也要遵守OS对应的规则。编译器编译我们的代码的时候,就是按照虚拟地址空间的方式进行对我们代码和数据的编址(32位),也就是说代码数据在磁盘上早就划分好了。当程序被加载到物理内存中的时候,天然拥有一个外部的物理地址。

可执行程序里两个方法 fun(use a;)地址是0x1234, main(){fun()}地址是0x1122,main函数内部调用了一个函数叫作fun(),在磁盘中还有一个全局变量 a = 10,它对应的地址是0x2222,这些地址是代码在编译期就有了所对应的逻辑地址。把磁盘上的代码数据加载到物理内存,数据自己本身就有地址fun函数用a的时候就能自己找到他,虚拟地址空间有代码段、未初始化、初始化,所以代码在物理内存中被加载,同时也就具备了物理地址。所以我们现在有两套地址,一套是标识物理内存中代码和数据的地址,另一套是在程序内部互相跳转的时候的虚拟地址。我们读取物理内存中的地址,eg读取main的物理地址。CPU从main函数开始跑,加载的是main函数的虚拟地址,此时查页表访问到物理地址,就调main函数,开始一步一步往后执行,我们要调fun函数,那么就是把fun函数对应的虚拟地址(CPU读的是指令,指令内部就有地址(虚拟地址))load到CPU中,读到0x1234,然后就通过页表映射开始调fun函数,fun函数要用a,那么我们就把use a的指令读取到CPU中,然后CPU读到a的地址0x2222,要进行的操作是++操作,然后通过地址空间找页表,然后映射到物理内存,找到全局变量a。在整个过程CPU压根没有见到物理地址

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

马尔科686

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值