虚拟内存是操作系统内存管理的一个重要技术,设想在两个不同的进程中有两个变量,他们的地址相同,那么这两个变量的值是否是相同的呢,显然这是不同的,不信你可以在linux下运行如下代码(windows下是无法编译的)
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<sys/wait.h>
#include<errno.h>
#include<stdio.h>
#include<stdlib.h>
int main(){
int a=1;
pid_t pid=fork();
if(pid<0){
perror("fork fail");
exit(1);
}
else if(pid==0){//子进程
a=2;
printf("child process a's address is %p,value is %d",&a,a);
exit(0);
}
else {//父进程
wait(NULL);
printf("paren process a's address is %p,value is %d",&a,a);
}
return 0;
}
在我的Linux虚拟机下运行结果如下
child process a s address is 0x7ffd0e29dd50,value is 2
paren process a s address is 0x7ffd0e29dd50,value is 1
可以看到两个进程中变量地址一样但值却不一样,怎么会是呢?
其实在运行的程序中变量地址并不是真实的内存物理地址,而是虚拟地址。每个进程都有232虚拟地址空间(32位机)或264虚拟地址空间(64位机)
为什么用虚拟地址而不用真实地址呢,这有很多原因,不细讲了自行百度。
那通过虚拟地址如何找到变量的真实物理地址呢。可以为每一个进程维护一个映射表,每一个虚拟地址在映射表都对应一个物理地址,这样查表即可得到真实物理地址了。但是这么做有一个问题,对于一个32位机来说,一个进程的有232虚拟地址空间,如果把每个虚拟地址都放入映射表中的话,那么表的大小将会有4Bytes232=16GB这么大,这内存开销太大了,比个人PC内存都要大,是不可能实现的。因此就有了分页技术。
什么是分页呢,简单来说就是把真实的物理内存分为固定大小的块,每一块称为帧或页帧,把逻辑内存也就是虚拟内存也分成同样大小的块,称为页或页面。我们假设每个页大小为212,也就是每212个地址组成一个页,那么一个虚拟内存中就有232/212=220个页,把这220个页放入映射表中,表中每一个页对应其真实帧的起始地址,就只需要4Bytes220=4M大小的内存了,现在内存开销变的可以接受。这样的映射表称为页表。当我们得到一个虚拟地址p[31:0]时,前20位p[31:12]称为页码,后十二位p[11:0]称为页偏移,我们在页表中通过页码来查找物理帧的起始地址,然后将物理帧起始地址与页偏移组合来得到该虚拟地址的真实物理地址,如下图所示