win-虚拟内存
虚拟内存
物理寻址和虚拟寻址
把物理内存分为M个连续的字节数组。分别标号为0,1,2…这就是物理地址。
CPU直接查找对应的物理地址来查找信息就是物理寻址
。CPU直接对内存总线发送地址然后主存接收到以后返回内容给CPU,并放在寄存器里。
现在,CPU一般先生成一个虚拟地址,然后将虚拟地址发送给MMU(内存管理单元)翻译成物理地址,这时才发送给主存,它再返回地址中的内容。这叫虚拟寻址
简要地址翻译流程:
在内存中存放了一个查询表(称为页表),MMU通过读取查询表来动态翻译虚拟地址,表的内容由操作系统管理。(一般情况下,第一级页表就在MMU中,被称为TLB,这样速度更快)
地址空间
一个非负整数地址的有序集合。意思是:用自然数从0开始依次为一个地方编号,得到的数的有序集合就是地址空间
。如果这个地址空间的元素是连续的整数,就称它为线性地址空间
。
现在,CPU通过映射n比特位得到了$2^n$个地址编号,形成的线性地址空间的大小是$2^n$。记作$N = 2^n$,最小地址是0,最大地址是$N-1$。这样形成的地址空间被叫做n位地址空间
。它表示虚拟内存,现在的32位系统/64位系统即是这个意思。
内存也有一个空间,它是物理存在的。它的大小记作M,不要求一定为2的幂次。
页
我们已经知道是从虚拟地址到物理地址的映射了,页的实现就是映射实现的具体方式。
为了让虚拟内存中的地址可以顺利转换到物理地址,我们规定每4KB长度的地址称为一个页
,和地址一样,分为虚拟页和物理页(物理页又称为页帧)。虚拟页面的集合为{未分配的页,已缓存的页,未缓存的页},后两项统称已分配的页。我们把一个虚拟也的大小称为$P=2^p$
而映射的基本单位就是一个页。也就是说,一个进程在内存中是被划分为n页分开存放的(也可以挨在一起)
缓存表示已经分配到物理内存中,而未缓存表示在其它存储介质中,比如硬盘。如果系统查询页时发现是未缓存页,这称为未命中(缺页),然后要把那个页和内存中的一个物理页交换以访问它
页表
系统为了查询哪些页是已经分配的,哪些页是没有缓存的,以及页缓存在哪里等信息,同硬件/软件一起在内存中维护了一张页表。每个页表都是一个页表条目(PTE)数组
页表中每一个虚拟地址都对应了一个物理地址,也可能对应其它硬件,比如磁盘的某个位置。同时设置了一些标志来判断部分情况(比如可能0表示没有映射)
缺页
如果cpu查询页表发现它是一个未缓存页,就会引发一个缺页异常
,从而进入内核态执行以下操作:
- 选择一个页表中查询次数最少的页作为牺牲页
- 如果它被修改了,那么写回磁盘
- 将这次查询的物理页写到牺牲页对应的物理页中
- 刷新页表,然后可以继续访问
内存管理
实际上,操作系统并不是只维护一张页表,而是对于每个进程都维护一张页表,每个的大小也都是4KB,从而形成了每个进程的独立虚拟地址空间。这样,每个程序的EP可以都是0x400000,而不用担心相互影响。
内存保护
页表中有访问权限的标志位可以控制对对应页的访问,综合段寄存器,实现对段页权限的划分,提供安全等级
地址翻译
在这里说明虚拟地址转为物理地址的流程
任何虚拟地址都由两部分组成(物理地址也是):
虚拟页号和页偏移,长度分别为n-p-1和p-1
转换时,只转换虚拟页号,转为物理地址后+页偏移就得到真实的物理地址
正如上面讲的,为了减少时间消耗,MMU中有一个翻译后备缓冲器TLB,它是一个物理设备,每一行都有一个由单个PTE组成的块,它可以直接被cpu查询
多级页表
很明显,如果是32位系统,由于一个程序就有一个4MB的页表(一个页表项需要4字节,有4GB/4KB=$2^{20}$项),如果开了50个进程,就有200MB大小的空间,这显然是不能忍受的。
于是,使用多级页表可以实现压缩内存。首先:第一级页表在内存中,最常用的在TLB中,检索地址时,先检索TLB,如果没有,再检索内存,然后转到二级页表,被称为片(chunk)。再在片中查找基址再转到虚拟内存。
如果第一级页表不存在对应PTE,第二级页表页不会存在。
第二级页表不一定在内存中,只有最常用的才在。
这样就减轻了内存的负担。
实际上,使用的层级还更多
参考:
书籍:深入理解计算机系统
网站: