高级处理器CPU一般都会实现内存管理单元(MMU),其也是Linux等高级操作系统运行的必备条件。虚拟内存管理是linux操作系统的基本组件之一,其目的是让每个应用程序都单独拥有足够大的(G字节级别)逻辑空间,并共享同一块较小的物理内存空间。虚拟内存管理正是依赖内存管理单元(MMU)来实现的。各进程在内存中的页表和MMU中的TLB(相当于页表的cache)是虚拟内存管理中的重要概念。

        在很多场景下的产品并不需要高级处理器那么强的计算能力,而基于控制器的SOC更具竞争力,更低功耗,更省成本!但是基于MMU的虚拟内存管理思想对于轻量化的多任务操作系统来说至关重要。假如,我们希望在基于控制器的SOC中实现MMU单元,然后通过定制操作系统的虚拟管理模块来实现内存管理,该怎么做呢?

 

一、内存管理单元(MMU)的工作机制

    在阐述控制器领域的内存管理设计之前,还是要先介绍处理器领域的虚拟内存管理机制,前者很大程度上是对后者核心机制精髓的借鉴。实现虚拟内存管理有几个模块是协调工作的:CPU、MMU、操作系统、物理内存,如图示(假设该芯片系列没有cache):

我们根据上图来分析一下CPU访问内存的过程,假设寻址是0x10000008,一页大小为4K(12比特)。则虚拟地址会分成两个部分:页映射部分(20bit,0x10000)+页内偏移(12bit, 0x8)。CPU通过总线把地址信号(0x10000008)送给MMU,MMU会把该地址的页映射部分(20bit)拿到TLB中匹配。

TLB是什么东西?Translation Lookaside Buffer,网上有称为“翻译后备缓冲器”。这个翻译都不知道它干什么。它的作用就是页表的缓冲,我喜欢叫它为页表cache。其结构图如下:

可以想象,TLB就是索引地址数组,数组的每个元素就是一个索引结构,包含虚拟页地址和物理页地址。其在芯片内部表现为寄存器形式,一般寄存器都是32位,实际上TLB中的页地址也是32位寄存器,只不过索引比较时是比较前20bit,后12bit其实也是有用的,例如可以设置某个bit是表示常驻的,即该索引是永远有效的,不能更换,这种场景一般是为适合一些性能要求特别高的编解码算法而设计的。非常驻内存的一般在某个时刻(如TLB填满时访问一个新的页地址)就会发生置换。

 

1)  假如 0x10000008的前20bit在TLB中第M个索引中命中,这时就表示该虚拟页在物理内存中已经给它分配好对应的物理内存,页表中也已经做好记录。至于虚拟地址对应的代码页是否从外存储(flash,card,硬盘)的程序中加载到内存中还需要要另外的标记,怎么标记呢?就是利用上面所讲的TLB低12位的某一bit(我们称为K)来标识,1标识代码数据已经加载到内存,0表示还没加载到内存。假如是1,那就会用M中的物理地址作为高20bit,以页内偏移0x8作为低12bit,形成一个物理地址,送到内存去访问。此时该次访问就会完成。

2)  假如 K是0,那意味着代码数据尚未加载到内存,这时MMU会向中断管理模块输出信号,触发一个中断进行内核态,由操作系统负责将对应的代码页加载到内存。并修改对应页表项的K比特和TLB对应项的K比特为1.

3)  假如 0x10000008的前20bit在TLB所有索引中都没有命中,则MMU也会向中断管理模块输出一个信号触发中断进入内核态,由操作系统将0x10000008右移12位(即除以4K)到页表中去取得对应的物理页值,假如物理页值非0有效,说明代码已经加载到内存了,这时将页表项的值填入到某一个空闲的TLB项中;假如物理页值为0,说明尚未给这个虚拟页分配实际的物理内存空间,这时会给它分配实际的物理内存,并写好页表的对应项(这时K是0),最后将这索引项写入TLB的其中一条。

2)和3)其实都是在中断内核态中完成的,为什么不一块做了呢?主要是因为一次中断不应该做太多事情,以加大中断延时,影响系统性能。当然如果有芯片将两者做成一个中断也是可以理解的。我们再来看看页表的结构。页表当然也可以按TLB那样做成索引数组,但是这样有两个不好的地方:

1)页表是要映射所有的虚拟页面的,其维护在内存中也需要不小的空间。页大小是4K时,那映射全部就是4G/4K=1M条索引,每条索引4*2=8个字节,就是8M内存。

2)假如按TLB那种结构,那匹配索引的过程就是一个for循环匹配电路,效率很低,要知道我们做这个都是在中断态完成的。

所以一般的页表都是设计成一维数组,即以整个线性虚拟地址空间按页为单位依次作为数组的下标,即页表的第一个字(4字节)就映射虚拟地址空间的最低4K,第二个字映射虚拟地址最低的第二个4K,以此类推,页表的第N个字就映射虚拟地址空间的第N个4K空间,即(N-1)*4K~4KN的地址空间。这样页表的大小就是1M*4=4M字节,而且匹配索引的时候只是一个偏移计算,非常快。

二、控制器领域SOC内存管理单元设计

在这里,贴出前公司同事(彭洪、江小炜)的专利,主要阐述了控制器SOC中内存管理单元的设计过程,我在基于MIPS核的SOC中整合并验证了该专利的可行性,并通过定制操作系统和重构应用程序来高效实现了虚拟管理。良好的软、硬件协同设计也是该系列芯片成功量产上亿颗的前提条件。

以下只是MMU的系统设计,有关适配该机制的虚拟内存管理和应用程序重构以后再做分解。大家也可以通过查看“SOC软件架构设计”系列学习更多的软硬件协同设计知识。专利有点长,关键的地方就在于寻址命中时要做什么,不命中时要做什么。