目录
作用1:进程地址空间的存在,保证了其他进程无法直接访问内存空间,保护了内存中代码和数据的安全性。
作用2:地址空间的存在,保证了进程与进程之间代码数据的解耦——写实拷贝。保证了进程间的独立性特性。
说作用3之前,再整体概述一下进程运行后与进程地址空间与物理内存的流程:
作用3:通过虚拟地址空间来屏蔽底层内存申请的过程,从而达到进程和OS进行内存管理操作,进行进程调度和内存管理相互解耦的操作。
作用4: 让进程能够以统一的视角看待进程对应的代码数据等各个区域,让编译器也能以统一的视角进行代码的编译。
作用5:进程地址空间的连续化区域划分,可以降低异常越界访问的概率。
之前,我讲述了有关进程地址空间的定义概念,通过列举的几个例子从各方面更加深刻的理解到了进程地址空间:( Linux进程地址空间——上篇_)
接下来让我们继续理解进程地址空间的重要性吧!
一.深入了解进程地址空间:
单个进程与进程地址空间与物理内存之间的联系图:
当磁盘中的可执行程序被加载到内存后,程序变为了进程,进程有了自己的PCB进程控制块(struct task_struct结构体),有了自己独立的进程地址空间(struct mm_struct结构体),该进程中的代码和数据也会被加载到内存中,因为进程地址空间中存放的是代码中各个变量和函数的虚拟地址,物理内存中存放的是各个变量和函数的物理地址,意味着变量和函数同时拥有两套地址,那么我们可以推论出:虚拟地址和物理地址是相互映射的。
既然是相互映射,就好比我们使用汉语字典,通过偏旁部首、拼音等找法可以查找到我们需要的那一个汉字的具体所在页的具体解释。所以页表就担当了此种重任!页表是用来连接物理地址和虚拟地址相互映射的一张表,使得CPU可以通过虚拟地址找到物理地址,从而找到进程所在内存中的代码和数据。
多个进程与进程地址空间与物理内存之间的联系图:
每个进程都认为它自己是独占内存空间的,都认为自己有4GB的物理内存空间(其实是4GB的虚拟地址空间),每个进程并不知道还有和自己一样的存在——操作系统画的大饼!
二.为什么会存在进程地址空间呢?
作用1:进程地址空间的存在,保证了其他进程无法直接访问内存空间,保护了内存中代码和数据的安全性。
根据上图,假如没有进程地址空间的话, 进程1可以直接访问物理内存,进入进程1的代码和数据后,完成相应的操作,但这也会有危险性,进程1可能会访问到进程2的代码数据,万一进程1是个伪装好的病毒进程,能够直接访问内存的话就会窃取到其他进程的数据资源,给用户造成极大的损失。
所以进程地址空间的存在就好比是物理内存空间的保安,你必须经过检验,合法了才能够访问物理内存。
注:检验是页表的事情,页表可不仅仅是用来虚拟地址映射物理地址的,还有检验拦截的作用,对于不合法的进程访问会进行拦截,它就无法访问到内存。
作用2:地址空间的存在,保证了进程与进程之间代码数据的解耦——写实拷贝。保证了进程间的独立性特性。
代码底层设计图解:
gloval作为全局变量,子进程在执行流中修改了全局变量的值,gloval变为了300,但是在父进程的执行流中仍然是gloval=100。
形成情况的主要原因就是:gloval作为全局变量是共享数据,当某个进程修改了被共享的数据时,操作系统会根据被修改的数据重新拷贝一份,重新给其开辟空间,供子进程修改,那么以后子进程访问到gloval就是在内存中新开辟拷贝下的数据地址了,而父进程访问到的仍是原gloval的数据地址不变!
这就体现了进程的独立性,多个进程之间的运行是互不影响的。
任何一方进程想要更改被进程多方共享的数据时,os操作系统都会对该方进程进行数据拷贝,形成新的物理地址,并且更改页表映射,最后让该进程进行修改。这一操作称为写时拷贝。
写实拷贝技术的原因:提高进程创建的效率,有可能多个进程只是读取共享的数据,并不改。 所以当执行到修改共享数据时,操作系统才会开辟新的内存物理地址,拷贝数据到该新物理地址中,供该进程修改。
说作用3之前,再整体概述一下进程运行后与进程地址空间与物理内存的流程:
可执行程序想要被执行,就得从磁盘被加载到内存中成为进程才行。成为进程后,操作系统会根据进程生成其PCB进程控制块,CPU不会执行进程,它只执行进程的PCB,CPU拿到进程PCB后,开始处理该进程的代码和数据。在处理过程中,CPU获取到了代码中的各种函数和变量的虚拟地址,这是不够的,它得去内存中找代码形成的逻辑,找到执行指令才能用继续运行。于是CPU通过寻找PCB的参数指针指向虚拟地址空间,在代码区,数据区或者堆栈区找到这些函数和变量的虚拟地址,将这些虚拟地址再通过页表映射找到在内存的物理地址。于是CPU成功在内存中找到了这些函数,但CPU不认识物理地址,于是让操作系统携带着内存的指令传回CPU去执行。
那么虚拟地址是如何产生的呢?
例:
cpu在运行该进程的PCB时,会执行进程里面的代码数据,例如用到变量a时,CPU会通过进程地址空间的虚拟地址,通过查找页表,映射到物理地址。在内存中,该变量a会将自己的虚拟地址再传回CPU中执行指令;当cpu用到main函数,fun函数时也是这样,cpu进入地址空间通过页表找到这些函数所在内存物理地址后,这些函数所涉及到的运算指令会进入CPU中执行相应的操作。
再举个生活上的例子加深理解:
作用3:通过虚拟地址空间来屏蔽底层内存申请的过程,从而达到进程和OS进行内存管理操作,进行进程调度和内存管理相互解耦的操作。
操作系统有四种核心管理:进程管理、内存管理、驱动管理、文件管理。
如果没有进程地址空间,进程直接访问物理内存,当进程退出时,内存管理需要尽快将该进程回收,在这个过程当中必须得保证内存管理得知道某个进程的退出信号,内存管理也得知道某个进程开始的信号,这样操作系统才能给它们及时的分配资源和回收资源,这就意味着内存管理和进程管理模块是强耦合的。
也就是说内存管理和进程管理联系比较大,通过我们上面的理解,如果有了进程地址空间,当一个进程需要资源的时候,通过页表映射去要就可以了,内存管理就只需要知道哪些内存区域(配置)是无效的,哪些是有效的(被页表映射的就是有效的,没有被页表映射的就是无效的),当一个进程退出时,它的映射关系也就没了,此时没有了映射关系,物理内存就将该进程的数据设置称无效的。
所以第二个好处就是将内存管理和进程管理进行解耦,内存管理是怎么知道有效还是无效的呢? 比如说在-块物理内存区域设置一个计数器count,当页表中有映射到这块区域时,count就++,当一个映射去掉时,就将count--,内存管理只需要检测这个count是不是0,如果为0,说明它是没人用的。
作用4: 让进程能够以统一的视角看待进程对应的代码数据等各个区域,让编译器也能以统一的视角进行代码的编译。
作用5:进程地址空间的连续化区域划分,可以降低异常越界访问的概率。