虚拟地址:
引子:
我们在Linux中使用fork函数创建子进程时是否会产生一下几个疑惑。
为什么一个变量id具有两个值?
学习C语言后,我们都知道一个函数是不会返回两个值的,那么我们只能猜测是fork函数return了两次。
事实也的确如此,当fork之后,父子进程就共享了一段代码,父进程return一次,子进程return一次,就当然有两个值了。
But还有一个疑问,第一次写入id的数据为什么没有被覆盖?或许我们会任务父子进程的id在两个作用域中互不影响,地址不同。
的确进程之间是具有独立性的,父子进程并不会相互影响,但两个进程中的id真的地址不同吗,实现以下就知。
两个地址居然一模一样,同一个地址为什么对应的数据不一样???
可以非常确定的是,物理内存上的每一个地址都对应着唯一的数据。
既然这样,我们可以断言,这里的地址绝对不是物理内存,更像是一种经过封装后的地址,我们成为虚拟地址
可以说几乎所有高级语言给我们看到地址都是虚拟地址,存储虚拟地址的容器我们成为地址空间,所谓地址空间简单地说是一种内核数据结构,在Linux下地址空间的划分非常简单,是在一个结构体中通过指针划分好几个区域,这些区域就是我们熟悉的栈区、堆区、静态区、常量区等
页表:
虚拟地址与物理地址的桥梁,既然不能直接得到物理地址,那么就需要借助第三方来帮助得到,这就是页表,页表是一种映射关系。
我们以数学中的f(x)函数为例,虚拟地址是自变量,物理地址是因变量,那么页表就是对应关系。
地址空间存在的原因:
1、早期的计算机确实是没有地址空间的概念的,都是直接访问物理内存的。但是这么做有一个致命的缺陷,就是安全性问题,由于物理内存是能够任意读写的,物理内存不知道什么是权限,进程之间互相干扰的可能性极大。
有了地址空间的存在,就能够把一些非法访问拦截住,在学习C的过程,你肯定出现过访问权限冲突的问题吧,这就是操作系统把你的行为阻止了。
2、由于页表映射关系的存在,物理内存中的数据可以处于一种无序的状态,因为都可以直接通过页表找到。大大减低了进程管理模块和内存管理模块的相互影响,起到了解耦合的作用。
3、因为每个进程都拥有自己的页表和地址空间,进程之间的联系就减弱了,体现了进程的独立性原则。
4、提高物理内存利用率,我们在动态申请空间后可能不会马上使用,操作系统便不会直接给我们物理空间,只是给你一个声明。
解释一开始的问题:
当我们创建子进程时,子进程会拷贝父进程的地址空间和页表,但是会对页表中id的虚拟地址和物理地址间的对应关系做出调整,使它们都有自己的物理地址,但是没有修改我们所看到的虚拟地址。
了解程序计数器:
CPU给每一个进程处理的时间是有限的,一旦一个进程的规定时长达到后,它会出让资源给下一个进程,自己回到运行队列尾部等待下一次CPU访问,从而达到进程并发,但是CPU怎么知道第二次访问该进程是从它对应的代码的哪一句运行的,这就需要程序计数器来告诉CPU了,程序计数器存储了上一次该进程暂停的位置。方便CPU快速定位。
学 到 这 里‘
故而我们可以对进程的概念做进一步拓展
进程:PCB+代码数据+页表