白话虚拟内存

在学习操作系统的路上,虚拟内存这个概念是我花了最多的时间才能做到完全理解,即使会做题了,但是还是没深刻体会到它的精妙之处。课本大多比较理论,本文从程序员角度入手。

1、地址空间

首先想一个问题,地址是干嘛的?毋庸置疑,地址是用来定位的,说到定位就有一个问题,因为位置是一个相对的概念。要事先说明你是位于哪个空间,或者哪个坐标系。日常生活中都用经纬度定位,但是其实这个经纬度也有他自己坐标系,只是日常生活中,都用同一个所以可以省略不讲。举个更接近生活的例子,你说你在7楼,那一定是某栋楼的7楼,如果大家都在同一栋楼你可以不用说你是哪栋楼的。否则你就是带上楼名,而这个楼名就是一种空间。所以说我们谈论内存地址的时候,其实就带了一个空间。

2、你是哪块内存上的地址?

回归计算机系统,操作系统是如何使用内存的呢?是不是很多初学者和我一样,就知道一句话,当计算机运行一个程序时会把程序从磁盘完全复制一份到内存然后开始运行,当知道这句话还以为掌握了什么惊天大秘密,然而在现代操作系统下,并非如此。先想一下,我们在写程序时,什么情况下会用到地址,c语言用&操作符可以取地址。观察下面一段程序,会输出什么?

#include <stdio.h>
int main(void) {
    int a=0;
    int *p=&a;
    printf("%p\n",p);
    return 0;
}

在我的机器上会输出0x7ffe105b1e58,大家觉得这个地址是哪个空间的地址呢?它是由12位十六进制组成,共48位二进制,众所周知,32位二进制可表示的范围是4G空间,那么48位能表示多大的空间呢?你的物理内存有这么大吗?不难发现他不是物理内存上的地址。我们姑且称他为虚拟地址!!!所表示的空间也是虚拟空间。进一步,如果再加一句

printf("%d\n", *p);

这条语句的含义是将p指向的地址所在存储介质上的状态以0或1的形式读出,再用%d也就是int形式编码输出,但是上面提到,该空间是一个虚拟的空间,也就没有所谓的状态,但是执行这条语句确实能输出a的值,这是怎么回事呢?

3、映射

依旧回想生活中的例子。当你去超市购物时,需要在超市存包处存包。存完包你会得到一个条形码,条形码上有一串数字,你如果去找这个编号的柜子,你大概率会找不到的,但是你把条形码一扫你的柜子就自动打开了。此时你的手中条码上的数字和柜子编号就存在一种映射关系,而扫码的设备负责存储这种映射关系。假设你手中的条码是61073210,对应的柜子编号是13号,那你让你朋友帮你把61073210里的东西取出来,其实取得是13号柜子的东西,往61073210放,其实也是放在13号柜子中。所以计算机也可以借鉴这种思想,上述的0x7ffe105b1e58为虚拟地址,其实会被MMU映射为一个物理地址。操作这个虚拟地址就相当于操作这个物理地址。

4、为什么需要虚拟地址?

两个进程操作一个相同的地址,假设同时操纵0x1000,进程A将该地址赋值为9,如果进程B将该地址赋值8,进程A再想访问该地址的时候发现已经被覆盖为8了,但假如都是虚拟地址,进程A将0x1000映射为物理内存的0x2000,进程B将0x1000映射到物理内存的0x3000,这样一来他们都是操作不同的物理内存,但是这些他们是不知道,有操作系统帮他们完成映射,所以每个程序员在写程序是根本不用考虑内存地址会不会被别人用,因为用的都是自己的虚拟空间,但是这个虚拟空间,编制方式和内存一样,也像是有一块内存一样,所以也就虚拟内存。这样就保证了每个进程都在自己空间内,保证了进程之间的隔离性。除此之外,假设你的程序有8G,但是你的物理内存只有4G,由于程序局部性原理,你就可以运行你程序0-4G不部分的时候,将他映射到你的物理内存,运行到4-8G的时候,更改映射表,又可以把你程序4-8G部分映射到物理内存,实际情况,粒度比这更细。4g内存却运行了8G的程序了,是不是很酷,有点时分复用的感觉,谁运行就映射谁!

本文比较宏观的介绍了虚拟内存,还有很多细节可以参考教科书。

 

posted @ 2022-09-14 01:07  Hello_Sewell  阅读(42)  评论(0)    收藏  举报