MMU(Memory Management Unit,内存管理单元)在现代处理器中扮演着非常重要的角色。操作系统通过使用处理器的MMU功能,能实现以下功能:
    1)虚拟内存。有了虚拟内存,可以在处理器上运行比实际物理内存大的应用程序。为了使用虚拟内存,操作系统通常要设置一个交换分区(通常是硬盘),通过将不活跃的内存中的数据放入交换分区,操作系统可以腾出其空间来为其它的程序服务。虚拟内存是通过虚拟地址来实现的。
    2)内存保护。根据需要对特定的内存区块的访问进行保护,通过这一功能,我们可以将特定的内存块设置成只读、只写或是可同时读写。
    
在嵌入式系统中,通常不会使用虚拟地址这一功能,因为它会使得任务的调度时间不具确定性。还有另一个原因就是,嵌入式系统的存储空间通常很小,有的只采用FLASH作为存储介质,并没有特定的空间用做交换分区。那在嵌入式系统中,MMU会用来做内存保护吗?是的,MMU在嵌入式系统中主要是用来做内存保护用的。还记得《程序中的段》中所提到的.text段和.rdata段吗?我们说这两个段通常是相邻放在连续的内存块中的,且这块内存会是只读的,而这就需要MMU来帮忙了!将这两个段所在的内存区块设置成只读的目的是,可以防止其内容被出错的程序意外的改写了。对于设置成只读的内存区当被意外的改写时,处理器会产生一个段异常,而操作系统则利用这一异常产生一个core(或dump)文件。通过这一文件,我们可以分析出是哪一段程序试图去改写只读内存的。应当说采用了内存保护的方法,可能使得程序出错时,我们找到问题的根源会相对的容易和快,因为其更接近出错的程序点。如果不具有内存保护功能,那么有可能程序去改写.text或.rdata段所在的区块时,还能运行,最后出错的地方可能离实际的出错点更为的“远”。
   
那是谁来负责将.text和.rdata段设置成只读的呢?通常这可以在我们的应用程序在初始化操作系统时完成。
   
对于MMU的一个特性我们在此不得不提及,MMU中存在页的概念,对于所有的MMU功能其操作都是以页的大小来进行的。比如,对于32位的x86处理器其页的大小是4K字节。在采用MMU设置内存的读写属性时,我们必须将页的起始地址作为设置参数,且这一地址必须是页的整数倍,k或者说地址必须是页对齐的。那从嵌入式系统开发的角度来看,我们需要注意什么呢?我们必须保护.text的起始地址必须是页对齐的,这是通过设置连接器的脚本来达到目的,即将.text段的起始地址设置成以页大小进行对齐,还有吗?先看图 1,为了解释方便我们假设.rdata段也在.text段中,且.text的起始地址已经采用连接器脚本设置成了页对齐的。

MMU的作用_嵌入式
图 1
我们说了对于MMU的操作是以页来进行的,那我们得保证.text的大小是页的整数倍,那如何保证?很简单,将.data段的起始地址也通过连接器脚本设置成页对齐就行了。这样看来,为了使用MMU,可能会造成一点点的内存空间浪费,因为当.text的大小不是页的整数倍时,我们得将其变成页的整数倍,即实际的.text段与.data段可能会有空隙。图 2中示例了将.text段和.data段的起始地址设置成页对齐。
MMU的作用_MMU_02
图 2

致读者

如果你想参与讨论嵌入式开发相关的话题,请加入技术圈(
g.51cto.com/UltraEmbedded)。