深入了解进程地址空间
一、进程地址空间
前面我们了解到,这张图并不是实际的地址,只是一个虚拟的地址,这个地址空间的本质其实时内核中一种数据类型。
我们称为struct mm_struct。每个进程都认为mm_struct代表了整个内存且地址位0x00 00 00 00,0xFF FF FF FF。
这个数据类型是当我们创建一个进程时,操作系统就会为这个进程单独设立一张独立的虚拟地址空间,这个虚拟的地址空间是进程独有的,这同时也侧面体现了进程的独立性。当进程拥有虚拟地址空间,那么在进程的眼里面就相当于自己拥有了4GB的空间,但实际上并没有,因为进程有的只是虚拟的4GB地址空间,当进程结束后,OS会进行回收这段空间来留给下一个进程使用。
我们前面提到了,虚拟地址空间的本事是内核中一种数据类型,地址空间由低到高从 【0x00 00 00 00,0xFF FF FF FF】32位寄存器,4*8=32嘛,虚拟地址空间进行各种区域划分时如:数据区,代码区,堆,栈等等,对应的其实是线性位置和区域。
接着我们来看上面这张完整的图,这张图很清晰的表明了虚拟地址和物理地址的联系,通过了页表联系起来,页表也划分成了两个区域,左边对应着虚拟地址,右边对应着物理地址,这样就可以很好的将虚拟地址映射到物理地址中去。这样看来是不是有点类似于哈希表的样子。
二、为什么有地址空间
那么我们为什么要弄一个虚拟地址空间呢?直接让进程操作物理内存不好吗?
答:1.我们通过添加一层软件层,有效完成了对进程操作内存做出了风险管理,本质目的是为了保护物理内存以及各个进程的数据安全。
现在我们举一个例子:在我们平常写代码时,可能会有申请1000字节这种操作,但是我们能立马使用者1000个字节吗?
答案是不一定,因为可能会存在暂时不会全部使用甚至不用这种情况。
那么站在OS的角度下,如果空间马上给你,你又不用,是不是意味着,整个系统会有一部分空间,本来可以给别人立马使用,现在却又被闲置这样的问题出现呢?这样的话会不会导致内存的浪费呢?
那么这个时候,OS就要出手了,而且有了前面提到的虚拟地址空间之后,OS就会进行“画饼”,具体操作就是给该进程在虚拟地址空间中开辟所需要的1000字节,但是不会在物理内存中开辟,这样在进程眼里,它的需求操作系统确实是满足了,当进程确实需要使用这写开辟的空间时,OS就会将在虚拟地址空间中开辟的映射到物理内存中去,来满足进程的需求。但是在整个一系列的操作中,OS做的内存申请动做都是透明的,对于进程来说都不知道。
根据上面的例子我们可以看到:为什么要有地址空间?
2.将内存申请和内存使用的概念在时间上划分清楚,通过虚拟空间来屏蔽底层申请内存过程中,达到进程读写内存和OS进行内存管理进行软件上的分离
那如果物理内存已经满了,这个时候OS还是会满足我们的请求,等真正我们需要使用这1000字节时,会进行物理内存和磁盘内存的相互交还。
现在我们站在CPU的角度来考虑一下设立虚拟内存有什么好处呢?
在我们平常会有很多进程同时存在,那么CPU是怎么找到这些进程的入口的呢?CPU怎么知道该进程应该从哪里开始处理呢??
答:这还是体现了虚拟地址和页表的重要性。当有两个不同main()函数入口,那么两个main()在物理内存里面肯定是在不同的位置,这时通过页表的映射将不同的main()函数的物理地址映射到相同的虚拟地址中去,那么此时,CPU就可以直接通过这同一个虚拟地址找到不同的main()函数,大大方便了CPU
那么总结一下上面的例子,为什么要有地址空间呢:
3.在CPU应用层的角度来看,进程统一可以看作统一使用4GB,而且每一空间区域的相对位置是比较确定的。
我们知道程序代码和数据可以被加载到物理内存的任意位置,这样可以大大的减少内存管理的负担。
OS这样设计的最终目的:就是让每一个进程都认为字节是独占系统资源的。
回答问题
在对上面有了那么多了解之后,现在我们来回答一下,我上一篇博客中提到的,为什么父子进程明明出现了两个不同的数据,打印他们的地址却还是一样的。
答:父子进程是两个进程,根据进程的独立性,两个进程都会有自己的虚拟地址,有自己的页表,而子进程又是继承了父进程的代码段和数据段,那么他们两个进程在虚拟地址空间中输出的地址是一样的,当改变数据之后,发生写时拷贝,OS会在物理内存内开辟一段新空间给子进程,但是子进程的虚拟地址还是不变的。相同地址的根本原因是它们所映射的物理地址并不相同,但虚拟地址不变。下面这张图就完美体现了这一点。
总结
本次博客总结了有关虚拟地址和物理的地址的深入关系。同时我们可以看到创建一个进程的工作量是很大的,需要为进程准备PCB,还要单独为进程准备一个虚拟内存空间大小为4GB,可能进程还有其他的要求等等,当然进程结束后,这些东西OS都要进行回收,这就涉及到了进程的控制,后面再提。
2023年 6月 15日