Linux 开发系列笔记(1.7) - 虚拟内存地址
建站提交历史文章,原文写作时间 2023 年 2 月前后。
虚拟内存地址
整理自:为什么需要虚拟内存?_小林coding的博客-CSDN博客
虚拟地址
为什么需要虚拟地址?
- 在做
单片机时,因为没有操作系统的存在,我们通常直接访问物理内存地址(Physical Memory Address),程序需要烧录到单片机中。如果在同一个单片机中,同时运行两个程序,那么这时就可能会出现各种错误。产生错误的原因就是为两个程序分配了包含相同的物理内存地址,两者的数据相互干预。 - 现代计算机
应用程序建立在操作系统之上,操作系统为我们完成了这部分冲突的解决,使得多道程序同时在操作系统上运行,而使得在应用程序开发中基本无需关心物理内存地址冲突的问题。 - 解决这一问题的技术
虚拟内存地址(Virtual Memory Address)、以及由此衍生出的内存分段(Segmentation)与内存分页(Paging)技术,就是本章讲解的内容。需要注意的是,虚拟内存由内核而无需用户关注,本章讲解的均为原理。
虚拟地址空间
-
虚拟内存映射表表存储于PCB中,每个进程都拥有一个虚拟内存映射表,不同进程的虚拟内存地址可以相同但物理内存地址通常不同。 -
在操作文件与内存中得到的所有地址都是
虚拟地址,物理地址对于用户是不可见的,也是无需关心的。 -
MMU:Memory Management Unit,通过获取来自进程虚拟内存映射表的信息,用于实现虚拟地址与物理地址之间的映射,从而完成CPU的内存管理请求。 -
虚拟内存映射表并不是直接给出物理内存地址,而是通过内存分段与内存分页实现,将在后面介绍。
内存分段
内存分段原理
内存分段是最早的虚拟内存地址技术,该虚拟地址由段选择因子、段内偏移量组成。内存分段会创建一段连续的长度可变的连续虚拟内存。CPU中的MMU获取虚拟地址,并得到段号与段内偏移量,段号将在段表中查找,获取段基地址(base)与段界限(bound),通过段基地址与段内偏移量获取准确物理地址。。
- 分段机制会将进程的
虚拟地址空间分为四个段:
内存分段缺陷
- 由于内存分段大小可调节、长连续地址的性质,内存分段往往产生大量
内存碎片且内存交换效率低。
例如:用户同时执行三个程序:游戏、浏览器、音乐。
- 在此情况下,如果物理内存充足,新进程可以向高地址载入内存,内存碎片一直存在;如果如图内存紧张,会进行
交换(swap)操作:将最近最少使用的段换出(swap out)到磁盘,换入等待使用的段,如果下次需要被换出的段可以重新换入。 - 由于内存与磁盘之间的
交换的IO操作速度极慢,而段的体积通常很大,内存分段原理造成的频繁交换操作很容易成为性能瓶颈。
内存分页
内存分页原理
- 与
内存分段不同的是,内存分页(Paging)具有固定的大小。Linux中页的大小固定为4KB。内存分页下的虚拟地址由页号与页内偏移量组成。内存分页的固定大小减小了交换体积与内存碎片。 CPU中的MMU获取虚拟地址,并得到页号与页内偏移量,页号将在页表中查找,通过物理页号与页内偏移量计算准确物理地址。。
简单分页缺陷
- 通常每个进程占用内存空间不受限制,直到将内存占满。页表需要为每个进程预留空间足够的大小,对于
32bit操作系统,地址空间为4GB,页表一条记录需要占用4B,每个页指向4KB空间,因此需要条记录,合计4MB。如果同时有100个进程运行在操作系统上,那将占用1/10的内存资源,这是一个无法接受的内存占用。对于64bit操作系统,需要支持更多空间,这个问题更加突出。 - 由此产生了
多级页表,用于解决这个问题。
多级页表
- 以
32bit系统为例,通常采用二级页表就能很好的解决上述问题。 一级页表通过一级页号指向个二级页号,由二级页号再指向物理内存。指向4GB的物理内存需要条二级页号记录,指向条二级页号需要条一级页号记录,可以发现合计4MB+4KB内存。然而,事实上物理内存不会被完全占用,对于多数进程其占用的内存远小于4GB,其一级页表多数是空的,而二级页表根据一级页表动态加载。一个二级页表的大小为4KB,一个进程通常加载不了几个二级页表。多个进程不可能同时占用4GB内存,这样一个进程的均摊页表占用为4KB~8KB。- 在
64bit系统中,二级页表显然不够用,通常使用四级页表,分别为:- 全局页目录项(
Page Global Directory) - 上层页目录项(
Page Upper Directory) - 中间页目录项(
Page Middle Directory) - 页表项(
Page Table Entry)
- 全局页目录项(
页表缓存(快表)
- 由于多级页表的存在,页表的层层访问自然减慢了访问效率,因此
页表缓存(TLB,Translation Lookaside Buffer,转址旁路缓存,后称快表)被加入。 - 频繁访问的页将被存入
快表中。TLB是一个高速缓存(cache),位于CPU与MMU近端。MMU查找物理内存地址时首先会在快表中查找,如未找到再在页表中查找。 - 事实上,
快表很大程度上提高了物理内存地址效率,因为常用的页表并不多。
段页式内存管理
内存分段与内存分页并不冲突。可以将进程分为多个有逻辑意义的端(如内存分段中的四段机制)指向连续的页表,再由页表指向物理内存地址。
Linux 内存管理
Linux的内存管理可以认为是分页式存储,但不可避免的涉及了分段式存储,这与Intel处理器的发展有关。- 如今被广泛应用的
X86处理器构架在Intel 80286使用的是分段式存储,由于不能满足需求,Intel 80386很快使用了分页式存储。但是Intel 80386实际使用的是段页式存储,虚拟内存地址首先通过段表映射到页表,再由页表映射到物理内存地址。 - 内核的开发需要处理器的支持,
Linux中主要使用分页式存储,由于历史原因,Linux不得不处理段表映射。 - 事实上,在
Linux中,每个段都是段基地址为0,段界限为4GB(32bit系统)的,这样实际上跳过了段机制。在Linux中,段机制只被用于访问控制与内存保护。
- 栈空间从高地址向低地址占用,堆空间从低地址向高地址占用。
评论
