青州建设局网站,做网站黑网站赚钱么么,中山外贸营销网站建设,WordPress优化速度插件文章目录进程地址空间奇怪的现象什么是进程地址空间#xff1f;#xff1f;#xff1f;虚拟地址是如何与物理内存联系的#xff1f;页表是什么呢#xff1f;为什么要有页表和地址空间#xff0c;让进程直接访问内存不行吗#xff1f;现象解释进程地址空间
在我们学习其…
文章目录进程地址空间奇怪的现象什么是进程地址空间虚拟地址是如何与物理内存联系的页表是什么呢为什么要有页表和地址空间让进程直接访问内存不行吗现象解释进程地址空间
在我们学习其他 编程语言时对于内存分布的概念是这样的 在Linux环境下可以验证一下。如下图所示 奇怪的现象
我们知道fork创建一个子进程子进程会继承父进程的代码和数据并以父进程为模板来创建自己那么子进程的码中的变量地址也是一样的吗
下面来验证一下
int main()
{if(fork() 0){int count 5;while(count){cout child- count: count getval: getval getval: getval endl;count--;sleep(2);if(count 3){cout ********** getval 50 ********** endl; getval 50;} }}else if(fork() 0){while(1){cout parent- getval : getval getval getval endl;sleep(2);} }return 0;
} fork创建的子进程中在不改变父进程的代码时他们共享的是同一份代码(继承)当子进程发生改变时会发生写实拷贝才会将父进程的内容复制一份给子进程
子进程和父进程因为写实拷贝值不一样但是为什么地址还是一样的呢
先说结论 变量内容不一样,所以父子进程输出的变量绝对不是同一个变量但地址值是一样的说明该地址绝对不是物理地址在Linux地址下这种地址叫做 虚拟地址我们在用C/C语言所看到的地址全部都是虚拟地址物理地址用户一概看不到由OS统一管理 OS必须负责将 虚拟地址 转化成 物理地址 。 什么是进程地址空间
进程地址空间其实是操作系统给进程画的一个大饼让每一个进程都认为自己是独占操作系统中的所有资源独占4GB
我们知道每个进程都会有一个task_struct(PCB)用来描述这个进程多个进程的PCB又会组织成数据结构队列、链表再由OS管理这个数据结构就可以间接管理到进程。
而在每个进程创建时都会创建一个进程地址空间struct mm_struct{}同时在PCB中创建一个mm_struct的指针也就可以对每个进程的地址空间管理了。
struct mm_struct就是操作系统给每个进程创建一个结构体用来记录每个进程中各个区域的开始位置起始位置等等。
如下图所示 虚拟地址是如何与物理内存联系的
这里又引入一个新的概念 “页表”
页表是什么呢 页表就是记录虚拟地址对应的物理地址的一张映射表由一些映射算法映射到物理地址上类似哈希表
页表还会存储各个区域划分的属性读/写权限等等
为什么要有页表和地址空间让进程直接访问内存不行吗
进程直接访问内存不是不可以而是会有很多隐患弊远远大于利这里简单分为三个问题来解释
1. 假如内存中加载了进程A、B、C进程B的代码有越界BUG而编译器不检查越界就会导致进程B的的代码可以访问到进程A或进程C并修改 例如一些恶意进程在我们手机中微信是进程A支付宝是进程B还有个恶意进程C由于中间没有任何干涉C进程直接访问内存恶意读取进程A、B的数据如支付密码、登陆密码、账户余额等等严重威胁信息安全 在进程添加页表、进程地址空间由操作系统管理而页表中的进程地址与物理地址的映射转换是由操作系统完成的也就可以由操作系统检查页表映射位置是否匹配权限是否匹配等问题 例如 当进程地址中越界了操作系统发现页表中没有对应到映射的物理地址就会挂掉它 字符量区是只读属性的当进程访问到常量区发生修改时操作系统发现页表对应的物理地址是只读属性就会挂掉该进程 2. 假如task_struct直接向内存申请了10000个字节空间但是他没有立刻全部使用只是使用了小部分当别人的进程申请空间时发现空间不够了 站在操作系统的角度如果空间申请后马上给到进程就意味着你闲置的空间本来可以给别的进程使用但是现在你不用也不许给别人用空间浪费了。 2.而操作系统管理页表和进程地址空间就可以改善这个问题 当进程申请空间时有可能是这样的 申请空间较小操作系统直接给它 申请空间较大操作系统不立马给只是先记在页表并给进程回话 “你的内存申请我通过了”等到进程读或写时操作系统才会去在页表中建立映射关系 例如当内存满了的时候 进程是不知道他满了的他申请内存时先记在了页表操作系统只是答应它但没有马上给他 等到进程去访问地址时操作系统会执行一些相关的内存管理算法把内存的整块的数据移至磁盘把移走的数据内存地址放在页表与进程空间地址建立映射或者在磁盘给他分一块空间 进程申请空间操作系统同意但是不一定马上给他或者给他的是磁盘空间当进程访问地址时才会去在页表中建立映射关系。这些进程都是不知道的他只负责访问自己的虚拟地址标号申请时记录在页表的虚拟地址部分 剩下的都由操作系统管理。 类似于酒店房间预定 CPU怎么知道代码的第一句在哪指main第一句 有了进程地址空间和页表可以规定把进程的代码中第一句放在页表指定位置mian的第一句 那么每个进程都把他们代码第一句放在同一个进程地址空间虚拟地址是连续的只需要记第一个位置即可CPU只去读取每个进程页表的0X1234即可找到每个进程的代码第一句的位置剩下的工作交给操作系统和页表 前面介绍的PCB上下文数据PCB运行队列的出口位置把top的PCB对应的进程的代码和数据加载到CPU中执行 同时也可以得出每个进程的数据和代码可以加载到内存的任意位置大大减少了管理内存的负担 总结
没有操作系统就算有页表和进程地址空间也做不到上面的改善因为只有被管理者没有管理者而只有操作系统没有页表和进程地址空间也无法管理这些进程与内存之间的关系因为只有管理者没有被管理者
通过添加一层软件层OS完成有效的对进程操作内存进行风险管理保护物理内存以及各个进程的数据安全将内存申请和内存使用的概念在时间上划分清楚通过虚拟地址空间来屏蔽底层申请内存的过程完成进程读写内存和OS进行内存管理的操作达到软件层面的分离站在CPU和应用层统一了每个进程的“4GB空间”使每个空间区域的相对位置确定同时做了空间连续化处理方便使用。 现象解释 回到最开始的代码就可以解释了为什么改变值后地址还是一样的
当fork创建完后子进程继承父进程的代码此时他们共享的是同一份等于页表中存放的地址相同 而当子进程发生改变此时会发生缺页中断和写实拷贝
先暂停子进程再把父进程在内存中的代码数据复制一份给子进程之后子进程立马修改对应的数据页表及时子进程对应的更新物理地址部分修改完成后再继续运行。
这时子进程和父进程才各自读取各自的数据 所以当不发生改变时共享一份资源发生改变时在复制一份给子进程减少了资源浪费 所以进程不仅是代码和数据更重要的是操作系统管理 —— 由数据描述的进程而组织成的数据结构PCB进程数据块组成的进程数据结构
struct task_struct{mm_struct* address、页表等等};都是在描述一个进程把每个进程的task_struct组成队列再由操作系统管理也就等于管理进程
再次证明管理的本质是“先描述在组织”