淘先锋技术网

首页 1 2 3 4 5 6 7


前言

以下内容基于80x86的linux虚拟机


一、虚拟内存优点

虚拟内存作为一种逻辑层,处于应用程序的内存请求与硬件内存管理单元(MMU)之间,虚拟内存的优点有:

  • 应用程序所需内存大于可用物理内存时也可以运行

      当物理内存不足是,虚拟内存也可以映射磁盘空间,但是对程序来说还以为自己在使用内存。
    
  • 程序只有部分代码装入内存时进程也可以执行

      当物理内存不足时可以将部分需要执行的代码放入物理内存,暂时不需要执行的代码放入磁盘。
    
  • 程序员可以编写与机器无关的代码,不用关心物理内存的组织结构

      当程序需要申请一块连续内存空间时,其对于的物理内存可能并非连续的,而程序直接操作虚拟内存,则可以当成连续内存来使用。
    
  • 为物理内存提供了保护机制,防止内核地址被用户进程访问,用户进程访问其他用户进程。

      在进程创建时内核会为进程维护一个页表,里面的每一行称为一个页表项,每个页表项包括虚拟内存对应的物理内存地址已经各种标志位,其中User/Supervisor标志位用来描述访问该物理内存需要的特权等级,如果值为1说明是用户进程地址空间任何时候该页框都可以访问,如果值为0说明是内核地址空间,只有切换为内核态时才能访问该内存;内核为进程维护页表时只会利用未被使用的物理内存,虚拟内存的低地址值表示页内偏移offset,所以进程通过虚拟内存访问内存不会影响其他进程。
    

二、基础名词

  • 线性地址:线性地址也称为虚拟地址,是一个32位无符号整数,可以用来表达高达4GB的地址,也就是高达4 294 967 296个内存单元。线性地址通常用16进制数字表示,值的范围从0x00000000到0xffffffff。
  • 物理地址:用于内存芯片级内存单元寻址。他们与从CPU的地址引脚发送到内存总线的电信号像对应。物理地址有32位或36位无符号整数表示。
  • 页:在虚拟内存技术中将虚拟地址空间被划分为固定大小的结构,这个结构称为页,一页通常4KB或8KB。
  • 页框:在虚拟内存技术中将物理内存也划分为固定大小的结构,这个结果称为页框,页框的大小和页保持一致。
  • 字节是存储的最小单元,页是IO的最小单元。

三、虚拟内存实现流程

在80x86的linux虚拟机中由于CPU的最大寻址范围是4GB所以进程的最大虚拟内存空间是4GB一般而言1-3GB为用户空间,3-4GB为内核空间,对于所以进程来说都是如此,所有进程的内核空间的虚拟地址和物理地址都是相同的,而用户空间的虚拟地址相同物理地址不同。原因是内核维持着一组自己使用的页表,这个组页表在系统初始化的时候被创建但不会被任何进程或内核进程使用,而是作为一个模板供进程页表创建时参考,换句话说进程页表的内核地址(虚拟地址范围0xc0000000-0xffffffff)相关的页表项是相同的。

  1. 当进程A被创建时内核参照内核空间页表以及进程A所占用的物理内存空间创建多级页表(全局页目录及各级页表)保存在内存中。
  2. 当进程A分配到CPU资源时内核将进程A的顶级页表地址放入寄存器cr3中,刷新MMU中的TLB缓存
  3. 当CPU访问进程A中的虚拟地址x时,CPU将内存转换请求传达给MMU,MMU首先看下TLB中有没有,如果有则将虚拟地址x对于物理地址y返回,并通过总线将y的读取指令传达给物理内存RAM,然后物理内存将地址y对于的数据通过总线传给CPU。
  4. 如果TLB中没有则MMU会根据寄存器cr3的值从内存中获取进程A的多级页表(内存中获取相对于TLB来说比较耗时),然后将虚拟地址x转换成2进制拆分成多段分别对于多级页表和页内偏移量,在页表中进行比对,找到对应的页表项。
  5. 查看页表项中的Present标志如果为1说明当前页在内存中,如果为0说明在磁盘中。同时比对User/Supervisor、Read/Write等标志判断是否有权限访问该页(虚拟地址)。
  6. 如果在内存中则将虚拟地址x对于物理地址y返回,同时将虚拟地址x与对于物理地址y的对应关系在TLB中做一份缓存,并通过总线将y的读取指令传达给物理内存RAM,然后物理内存将地址y对于的数据通过总线传给CPU。
  7. 如果当前页在磁盘中则此时会触发一个缺页异常/缺页中断,中断程序通过CPU给对于磁盘管理器发送读取指令进行IO读取,将读取到内存后的物理内存地址更新到页表中并重新执行虚拟地址x的获取指令即重复过程3。

四、多级页表和页表项的标志位

1、页表及页表项的标志位

1.1、一级页表

下图为虚拟内存基于一级页表查找物理内存地址的过程。MMU会将一个32位的虚拟地址分成两部分,由于一页4KB=4096个字节,而字节作为最小的储存单元,所以32位虚拟地址需要划分出12位用于页内字节寻址,剩下的20位高位地址用于和页表比对获取页框起始地址(物理地址),所以虚拟地址P+d基于页表转换成物理地址为R+d。如果前20位在页表中找不到对应页框说明为非法地址访问会抛出异常/触发中断,由对应中断程序处理,这就是虚拟内存机制对物理内存保护的体现防止一个进程的地址空间被其他进程破坏。
![在这里插入图片描](https://img-blog.csdnimg.cn/db1bc311a48e418fb4eb335746f28a9e.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA6Lq65bmz56iL5bqP54y_,size_20,color_FFFFFF,t_70,g_se,x_16在这里插入图片描述
上图表格为页表,每一行为一个页表项

1.2、页表项中的标志位

在这里插入图片描述
页表项一般为4字节32位包括:

  • Present标志(上图0位):1代表页框对于的物理地址在内存中;0代表不在内存中,MMU在读取页表项是如果发现present为0则会触发缺页中断。
  • Field标志(上图12-31位):存储页框对于的起始物理地址,由于每一个页框有4KB的容量,所以虚拟地址的后12位记录的是页框内的偏移,所以这里Field只需要记录虚拟地址的高20位对应的物理地址即可。
  • Accessed标志(上图5位):每当CPU对一个页框进行读操作时标记该值,这个值不是由MMU设置,而是由操作系统(CPU)设置。
  • Dirty标志:每当CPU对一个页框进行写操作时标记该值,这个值不是由MMU设置,而是由操作系统(CPU)设置。
  • Read/Write标志(上图1号位):记录该页框是否可读可写。
  • User/Supervisor标志(上图2号位):如果值为1说明是用户进程地址空间任何时候该页框都可以访问,如果值为0说明是内核地址空间,只有切换为内核态时才能访问该内存。
  • PCD/PWT标志(上图3.4号位):记录是否写入高速缓存TLB
  • PageSize标志(上图7号位):如果设置为1,则该页框代表2MB或4MB的大页框
  • Global标志: 用来防止常用页从TLB高速缓存中刷新出去,只有在cr4寄存器的页全局启用标志置位时这个标志才起作用。

2、为什么要有多级页表

在现代操作系统中页表都是多级存在的,在80x86的linux虚拟机中进程的多级页表分别是:页全局目录(CPU寄存器cr3中的值)——>页上级目录——>页中间目录——>页表——>页(offset)。
首先看下一级页表的弊端,4GB的虚拟地址高20位需要2^20个页表项,每个页表项4个字节共需要4MB内存,即一个拥有4GB虚拟内存的进程光页表就需要占用4MB的物理内存,所以需要采用多级页表来减少页表对内存的占用。
多级页表结构

五、TLB

1、什么是TLB

  • 在80x86Linux虚拟机中MMU还包含一个称为转换后援缓冲器或TLB的高速缓存用于加速虚拟地址的线性转换,当一个虚拟地址第一次使用时,通过慢访问内存中的页表计算出相应的物理地址。同时,物理地址被存放在一个TLB表项中,以便以后对同一虚拟地址的引用可以快速得到转换。
  • 在多处理系统中(每个CPU都对应一个MMU内存管理单元),每个CPU都有自己的TLB,这叫做该CPU的本地TLB。多个CPU之间的TLB中对应项不必同步,这是因为运行在各个CPU上的不同进程可能虚拟地址相同但映射的物理地址不同,因为每个进程都有单独的页表。
  • 当CPU的cr3控制器被修改时(进程切换时会修改),内核使当前CPU中的TLB失效,这是因为新的一组页表被启用而TLB指向的是旧数据。CPU不能自动刷新它们自己TLB,因为决定虚拟地址和物理地址直接何时不再有效的是内核而不是硬件。

2、TLB的刷新机制

2.1、TLB刷新的时机

  • 改变内核维持的页表时,由于内核页表作为所有进程内核虚拟地址的映射页表模板,所有影响所有进程的页表,所以此时全部CPU的TBL失效刷新
  • 更换一个范围内内核页表时,由于内核页表作为所有进程内核虚拟地址的映射页表模板,所有影响所有进程的页表,所以此时全部CPU的指定虚拟地址范围内的TLB失效刷新
  • 执行进程切换时(cr3被修改),运行当前进程的CPU中非全局页(全局页是指内核地址空间相关页,非全局指进程私有地址空间相关页)相关的TLB失效刷新
  • 触发缺页异常时,运行当前进程的CPU中单个页表相关的TLB失效刷新。

2.2、cr3寄存器被改变但不触发TLB失效的特殊情况

在进程切换时一般情况下会将CPU的cr3寄存器的值修改为新进程的页全局目录的地址,且当前CPU的TLB会被刷新。不过内核在下列情况下进行进程切换时会避免修改cr3刷新TLB:

  • 相同进程里的线程切换时不会修改cr3寄存器
  • 当两个使用相同页表集的普通进程直接进行系统切换时,由于页表集相同,所以不会修改cr3
  • 当一个普通进程和一个内核线程间执行进程切换时,内核线程并不拥有自己的页表集,更确切的说内核线程使用刚在CPU上执行过的普通进程的页表集,所以不会修改cr3

2.3、懒惰TLB

惰性TLB的目的就是为了避免多处理器系统上无用的TLB刷新,保证懒惰TLB模式下CPU可以专心执行内核进程,不会接受其他与TLB刷新相关的 * 处理器间中断 * 请求
当内核为某个用户态进程分配页框并把页框的物理地址存入页表项时(缺页中断需要完成的事情),它必须刷新本地CPU相应页的TLB区域,在多处理系统中如果有多个CPU正在使用相同的页表集,那么内核还必须刷新这些CPU上使用相同页表集的TLB区域。惰性是指,当CPU收到刷新TLB的指令时,如果此时CPU整处于内核态运行一个内核线程时,TLB的刷新将推迟到切换成用户态后再执行,因为内核态线程几乎不会访问用户进程地址空间。如果CPU处于用户态将即刻刷新对应页的TLB区域。

例子:进程A,B拥有相同的页表集,CPU1正在执行进程A但是遇到了硬件中断切换到内核态运行一个内核线程(硬件中断程序),此时CPU2正在执行B并触发了缺页中断,为进程B分配了新的页框,CPU2的TLB对应区域刷新。

  • 此时当CPU1收到刷新TLB请求时如果 不考虑惰性刷新时 TLB可能刷新1-2次
1. 立即刷新TLB
2. 硬件中断处理完后还是切换成进程B时TLB不刷新,硬件中断处理完后切换成进程C时TLB刷新
  • 此时当CPU1收到刷新TLB请求时如果 考虑惰性刷新时 TLB可能刷新1次
1.硬件中断处理完后还是切换成进程B时延时刷新TLB,硬件中断处理完后切换成进程C时TLB刷新