关键字 MMU、TLB、多任务OS、地址空间、虚拟内存、虚拟地址/物理地址
学习顺序 问题的引出、虚拟地址和物理地址、虚拟内存、OS进程和MMU,MMU运行
引子
早期的打算机PC,或者现在利用8位/16位MCU(单片机)的嵌入式设备,程序是直接运行在物理内存上的(SDRAM或者NOR Flash)。所谓直接运行在物理内存上,是指程序在运行时所访问的地址都是物理地址。例如,程序计数器PC中的值便是预取指令所在的物理内存的地址值。
这种程序直接运行在物理内存上的办法大略,但是并不适应于繁芜的系统,尤其是拥有多任务的OS。我们首先看看原来的办法有哪些不敷和毛病。
物理内存不敷。例如,某个程序运行须要64K的内存,而机器上只有32K的物理内存。程序运行的地址不愿定。同一个程序,每次被装载到内存的地址可能不一样。内存利用率低。须要运行某个程序,就须要将全体程序装入内存才能够运行。对付多任务OS,存在进程间地址空间不隔离的问题。这样一个任务失落败了,可能会导致全体系统宕机。于是人们就引入了虚拟内存管理(Virtual Memory Management)技能。有关虚拟内存管理(Virtual Memory Management)技能不才面会有详细的先容。须要解释的是,上面的几点毛病除了第一点之外,其它都是针对有OS的系统而言的。 虚拟内存管理技能的涌现和操作系统的发展有实质的联系。 本节可以参考《程序员的自我教化-链接、装载与库》第1章 1.5 内存不足怎么办。
虚拟内存管理技能
随着打算机的发展,运用程序的规模逐渐增大,一个难题涌如今程序员的面前,那便是运用程序太大以至于内存容纳不下该程序,常日办理的办法是把程序分割成许多称为覆盖块(overlay)的片段。覆盖块0首先运行,结束时他将调用另一个覆盖块。虽然覆盖块的交流是由OS完成的,但是必须先由程序员把程序前辈行分割,这是一个费时费力的事情,而且相称呆板。人们必须找到更好的办法从根本上办理这个问题。
人们找到了其余一个办法,这便是虚拟内存管理(Virtual Memory Management)技能。虚拟内存管理技能的基本思想是程序,数据,堆栈的总的大小可以超过物理存储器的大小,操作系统把当前利用的部分保留在内存中,而把其他未被利用的部分保存在磁盘上。比如对一个16MB的程序和一个内存只有4MB的机器,OS通过选择,可以决定各个时候将哪4M的内容保留在内存中,并在须要时在内存和磁盘间交流程序片段,这样就可以把这个16M的程序运行在一个只具有4M内存机器上了。而这个16M的程序在运行前不必由程序员进行分割。须要解释的一点,操作系统的内核是常驻内存的。
环绕着虚拟内存管理(Virtual Memory Management)技能,就产生了分页技能,虚拟地址,地址空间,TLB,MMU等观点。
分页技能
虚拟内存管理技能中最常见的是分页(paging)技能,MMU便是该技能的硬件实现。虚拟地址空间划分成称为页(page)的单位,而相应的物理地址空间也被进行划分,单位是页框(frame),页和页框的大小必须相同。接下来合营图片以一个例子解释页与页框之间在MMU的调度下是如何进行映射。
在这个例子中我们有一台可以天生16位地址的机器,它的虚拟地址范围从0x0000~0xFFFF(64K),而这台机器只有32K的物理内存。它可以运行64K的程序,但该程序不能一次性调入内存运行。这台机器必须有一个达到可以存放64K程序的外部存储器(例如磁盘或是FLASH),以担保程序片段在须要时可以被调用。在这个例子中,页的大小为4K,页框大小与页相同(这点是必须担保的,内存和外围存储器之间的传输总是以页为单位的),对应64K的虚拟地址和32K的物理内存,他们分别包含了16个页和8个页框。
我们先根据上图阐明一下分页后要用到的几个术语,在上面我们已经打仗了页和页框,上图中绿色部分是物理空间,个中每一格表示一个页框。橘黄色部分是虚拟空间,每一格表示一个页,它由两部分组成,分别是Frame Index(页框索引)和位p(present 存在位),Frame Index的意义很明显,它指出本页是往哪个物理页框进行映射的,位p的意义则是指出本页的映射是否有效,如上图,当某个页并没有被映射时(或称“映射无效”,Frame Index部分为X),该位为0,映射有效则该位为1。
我们实行下面这些指令(本例子的指令不针对任何特定机型,都是伪指令)
例1:MOVE REG,0 //将地址0x0000的值通报进寄存器REG.
虚拟地址0将被送往MMU,MMU看到该虚地址落在页0范围内(页0范围是0到4095),从上图我们看到页0所映射的页框为2(页框2的地址范围是8192到12287),因此MMU将该虚拟地址转化为物理地址8192,并把地址8192送到地址总线上。内存对MMU的映射一无所知,它只看到一个对地址8192的读要求并实行它。MMU从而把0到4096的虚拟地址映射到8192到12287的物理地址。
例2:MOVE REG,20500
虚拟地址20500在虚页5(虚拟地址范围是20480到24575)距开头20个字节处,虚页5映射到页框3(页框3的地址范围是 12288到16383),于是被映射到物理地址12288+20=12308。
通过适当的设置MMU,可以把16个页隐射到8个页框中的任何一个,这可以办理上面提到的第二个问题程序运行的地址不愿定,但是这个方法并没有有效的办理虚拟地址空间比物理地址空间大的问题。从上图中我们可以看到,我们只有8个页框(物理地址),但我们有16个页(虚拟地址),以是我们只能把16个页中的8个进行有效的映射。我们看看下面发生什么情形。
例3:MOV REG,32780
虚拟地址32780落在页8的范围内,从上图中我们看到页8没有被有效的进行映射(该页被打上X),这是又会发生什么?MMU把稳到这个页没有被映射,于是关照CPU发生一个 缺页故障(page fault) 。这种情形下操作系统必须处理这个页故障,它必须从8个物理页框中找到1个当前很少被利用的页框并把该页框的内容写入外围存储器(这个动作被称为page copy),把对应虚拟地址处的程序从外围存储器拷贝到刚才腾空的页框中,随后把须要引用的页(页8)映射到刚才开释的页框中(这个动作称为修正映射关系),然后重新实行产生故障的指令(MOV REG,32780)。假设操作系统决定开释页框1,那么它将把页8装入物理地址的4-8K,并做两处修正:首先把标记页1未被映射(原来虚页1是被影射到页框1的),以使往后任何对虚拟地址4K到8K的访问都引起页故障而使操作系统做出适当的动作(这个动作正是我们现在在谈论的),其次把页8对应的页框号由X变为1,因此重新实行MOV REG,32780时,MMU将把32780映射为4108。
正如本节开头所述,MMU是分页技能的硬件实现。我们大致理解了MMU在我们的机器中扮演了什么角色以及它基本的事情内容是什么。把稳,本例中的MMU并无针对某种特定的机型,它是所有MMU事情的一个抽象。
虚拟内存管理与OS
要理解内存在程序中是如何分配的,首先须要理解操作系统如何将内存分配给程序。打算机上的每一个进程都认为自己可以访问所有的物理内存。显然,由于同时在运行多个程序,以是每个进程不可能拥有全部内存。实际上,这些进程利用的是虚拟内存。
举个例子,让我们假定您的程序正在访问地址为 629 的内存。不过,虚拟内存系统不须要将其存储在位置为 629 的 RAM 中。实际上,它乃至可以不在 RAM 中 ―― 如果物理RAM已经满了,它乃至可能已经被转移到硬盘上!
由于这类地址不必反响内存所在的物理位置,以是它们被称为虚拟内存。操作系统坚持着一个虚拟地址到物理地址的转换的表,以便打算机硬件可以精确地相应地址要求。如果地址在硬盘上而不是在RAM中,那么操作系统将暂时停滞您的进程,将其他内存转存到硬盘中,从硬盘上加载被要求的内存, 然后再重新启动您的进程。这样,每个进程都得到了自己可以利用的地址空间,可以访问比您物理上安装的内存更多的内存。
在32-位的x86系统上,每一个进程可以访问4GB内存。现在,大部分人的系统上并没有4GB内存,纵然您将swap也算上, 每个进程所利用的内存也肯定少于 4 GB。因此,当加载一个进程时, 它会得到一个取决于某个称为系统中断点(system break)的特定地址的初始内存分配。该地址之后是未被映射的内存 ――用于在RAM或者硬盘中没有分配相应物理位置的内存。因此,如果一个进程运行超出了它初始分配的内存,那么它必须要求操作系统“映射进来(map in)”更多的内存。(映射是一个表示逐一对应关系的数学术语 ―― 当内存的虚拟地址有一个对应的物理地址来存储内存内容时,该内存将被映射。)
基于UNIX的系统有两个可映射到物理内存中的基本系统调用:
brk: brk() 是一个非常大略的系统调用。 还记得系统中断点吗?该位置是进程映射的内存边界。 brk() 只是大略地 将这个位置向前或者向后移动,就可以向进程添加内存或者从进程取走内存。mmap: mmap(),或者说是“内存映像”,类似于 brk(),但是更为灵巧。首先,它可以映射任何位置的内存, 而不单单只局限于进程。其次,它不仅可以将虚拟地址映射到物理的 RAM 或者 swap,它还可以将 它们映射到文件和文件位置,这样,读写内存将对文件中的数据进行读写。不过,在这里,我们只关心 mmap 向进程添加被映射的内存的能力。 munmap() 所做的事情与 mmap() 相反。如您所见, brk() 或者 mmap() 都可以用来向我们的 进程添加额外的虚拟内存。在我们的例子中将利用 brk(),由于它更大略,更通用。
摘自Jonathan Bartlett《内存管理底细-动态分配的选择、折衷和实现》。
虚拟/物理地址空间
地址空间是一个抽象的观点,由CPU体系架构的地址总线决定,一样平常等同于CPU的寻址范围、x位处理器中的x。地址空间一样平常分为 虚拟地址空间 和 物理地址空间 。
任何时候,打算机上都存在一个程序能够访问的地址凑集,我们称之为地址空间。这个空间的大小由CPU的位数决定,例如一个32位的CPU,它的地址范围是0~0xFFFFFFFF (4G),而对付一个64位的CPU,它的地址范围为0~0xFFFFFFFFFFFFFFFF (64T)。这个空间便是我们的程序能够产生的地址范围,我们把这个地址范围称为 虚拟地址空间 ,该空间中的某一个地址我们称之为虚拟地址。与虚拟地址空间和虚拟地址相对应的则是物理地址空间和物理地址,大多数时候我们的系统所具备的物理地址空间只是虚拟地址空间的一个子集,这里举一个最大略的例子直不雅观地解释这两者,对付一台内存为256MB的32bit x86主机来说,它的虚拟地址空间范围是0~0xFFFFFFFF(4G),而物理地址空间范围是0x000000000~0x0FFFFFFF(256MB)。
虚拟地址又被简称为虚地址,物理地址又被称为实地址。虚拟地址和物理地址之间的转换,又称为虚实地址转化。
在没有利用的虚拟内存管理(Virtual Memory Management)技能机器上,虚拟地址被直接送到内存总线上,使具有相同地址的物理存储器被读写。而在利用了虚拟存储器的情形下,虚拟地址不是被直接送到内存地址总线上,而是送到内存管理单元MMU。他由一个或一组芯片组成,一样平常存在与协处理器中,其功能是把虚拟地址映射为物理地址。
下面这张图便是描述虚拟地址/物理地址和CPU核、MMU、内存之间的关系。
虚拟内存管理与TLB/MMU
虚拟内存管理(Virtual Memory Management)技能中主要的一点,便是将地址空间分成了虚拟地址和物理地址,在CPU核和内存之间的地址总线上增加了一层。首先须要指出的是,TLB和MMU只是实现虚拟地址到物理地址转化最为常见的一种办法,还有其余的方法,如下文所描述。
为了区分不同进程的存储空间,现在多任务的操作系统以及处理器都须要支持虚拟地址(Virtual Address, VA)物理地址(Physical Address, PA)转化,地址转换紧张分为两种:
由于全体系统的进程数不定,每个进程所须要的内存不定,以及进程切换的不愿定性,因此,虚实地址转化不能大略的将某个连续大内存块映射到某个进程(Coarse-grained),必须采纳更细粒度(Final-grained)的映射,即将一些可能不连续的小内存块(比如4K大小)一起映射到进程,形成一块连续的虚拟地址。为了记录这些映射信息,须要页表(Page)。但是页表的导入引入了新的问题,那便是每次访存变成了两次,一次查询页表,得到物理地址,第二次通过物理地址取数(事实上有办法把这两个过程部分并行起来)。为了提高查询页表的速率,现在的处理器都为页表做了一个小Cache,叫做旁路转换缓冲(Translation lookaside buffer, TLB)。直接映射,比如直接将64位的虚拟地址高位抹去,得到物理地址。这紧张用于操作系统启动时的那块内存区域。紧张是由于系统刚启动时,第1种转化所须要的页表,TLB没有初始化(页表,TLB实在都是操作系统管理的,倘若还用第一种,就陷入了鸡生蛋,蛋生鸡的去世循环了),只能用这种最大略粗暴的办法。摘自《处理器的存储子系统》by Sigma。直接映射的办法在MIPS内存模型中的kseg0和kseg1便是利用的这种办法,直接将虚拟地址的高1位或者3位去掉,转化成物理地址。参考和《See MIPS Run》p47。
下面我们只先容利用MMU和TLB的映射事理。
MMU事情事理
MMU是如何把VA映射成PA的呢?内存中保存一张VA转PA的表,MMU被配置为指向该表的开始的物理地址,给一个VA通过MMU查表就可以查到PA,这是从大略处理解。实际上并不是这么大略,常日要有一个多级的查表过程,对付ARM体系构造是两级查表,对付一些64位体系构造则须要更多级。MMU的事情事理和机制,参考下面的各处理器架构MMU实例章节。
X86的MMU实例
以32位的x86系统为例。
x86采取两级页表构造,第一级称为页目录表,第二级称为页表。
名称类型VA索引bit位页表项存储大小/KB默认页大小支持的页大小页目录表L110位 [31:22]10244xxxxxx页表L210位 [21:12]102444KBxxx
页表目录的起始物理地址由CPU中的CR3寄存器指定。ARM920T的MMU实例
重点参考《ARM嵌入式系统开拓-软件设计与优化》第14章 存储管理单元。下面的利用的截图是三星公司的S3C2410,这款芯片是一种很常见的采取ARM920T的芯片。
ARM920T的MMU和Cache都集成在CP15协处理器中,MMU和Cache的联系非常密切。
下面是CP15协处理器的寄存器列表,和CPU核的r0到r15寄存器一样,协处理器寄存器也是用0到15来编号,在指令中用4个bit来表示寄存器编号,有些协处理器寄存器有影子寄存器。
CP15:c2寄存器保存转化表基地址TTB(Translation Table Base address)――指向L1主页表在物理内存中的位置。TTB地址必须与存储器的16KB边界对齐(寄存器的bit0 - bit13 为0,should be zero)。
对CP15协处理器的操作利用mcr和mrc两条协处理器指令,这两条指令的记法是从后往前看:mcr是把r(CPU核寄存器)中的数据传送到c(协处理器寄存器)中,mrc则是把c(协处理器寄存器)中的数据传送到r(CPU核寄存器)中。对CP15协处理器的所有操作都是通过CPU核寄存器和CP15寄存器之间交流数据来完成的。
Translation Table Walk
这个过程称为Translation Table Walk,Walk这个词用得非常形象。从TTB走到一级页表,又走到二级页表,又走到物理页面, 一次寻址实在是三次访问物理内存。 把稳这个“走”的过程完备是硬件做的,每次CPU寻址时MMU就自动完成以上四步,不须要编写指令指示MMU去做,条件是操作系统要掩护页表项的精确性,每次分配内存时填写相应的页表项,每次开释内存时打消相应的页表项,在必要的时候分配或开释全体页表。
ARM MMU
ARM MMU硬件采取2级页表构造:一级页表(L1)和二级页表(L2)。
L1页表只有一个主页表,也称为L1主页表(L1 master page table)或者段页表(section page table)。利用VA的高12bit位索引该表,以是该表有2的12次方(4K)个页表项(PTE,page table entry),每个页表项4个字节,一共须要占去内存16KB。
有两种类型的L2页表,分别是L2粗页表(coarse page table)和L2细页表(fine page table)。对付L2粗页表,利用VA的次高8bit索引该表,以是该表有2的8次方(256)个页表项(PTE,page table entry),每个页表项4个字节,一共须要占去内存1KB。对付L2细页表,利用VA的次高10bit索引该表,以是该表有2的10次方(1024)个页表项(PTE,page table entry),每个页表项4个字节,一共须要占去内存4KB。
名称类型VA索引bit位页表项存储大小/KB默认页大小支持的页大小主页表/段页表L112位 [31:20]4096161MB1MB细页表L210位 [19:10]102441KB1/4/64KB粗页表L28位 [19:12]25614KB4/64KB
下图是利用L1主页表和粗页表实现的一个大略VA到PA的转化示意图:
多级页表的意义
对付OS来说,页表是进程私有的,因此各个进程的页表被放置在不同的物理内存中。在进程启动或者切换时,都必须把进程的页表的起始物理地址见告MMU,也便是上面提到的TTB值(CP15:c2寄存器)。下面看采取单级页表的情形。假设页的大小为4KB,那么对付页表的索引就须要利用VA的[31:12],也就有1M个页表项,一个页表项4字节,可以算的页表的大小为4M。 这意味着这4M空间必须作为进程的必备资源在启动的时候一次分配,而且这4M的内存必须在物理地址上连续。 这和虚拟内存设计的理念(小内存系统上运行只管即便多的程序)不相符合。办理的办法便是采取多级页表。
多级页表是如何办理单级页表存在的问题呢?考虑到一个进程不须要同时访问到4GB的内存,因此把页表也分散开来,一级页表必须分配,二级页表像物理页面那样在须要的时候再分配映射。当然,多级页表须要MMU硬件支持。
MIPS32的MMU实例
待续
MMU和Cache的关系
待续