• Linux_MMU


    MMU是Memory Management Unit的缩写,中文名是内存管理单元,它是中央处理器(CPU)中用来管理虚拟存储器、物理存储器的控制线路,同时也负责虚拟地址映射为物理地址,以及提供硬件机制的内存访问授权。

    编辑本段历史

    许多年以前,当人们还在使用DOS或是更古老的操作系统的时候,计算机的内存还非常小,一般都是以K为单位进行计算,相应的,当时的程序规模也不大,所以内存容量虽然小,但还是可以容纳当时的程序。但随着图形界面的兴起还有用户需求的不断增大,应用程序的规模也随之膨胀起来,终于一个难题出现在程序员的面前,那就是应用程序太大以至于内存容纳不下该程序,通常解决的办法是把程序分割成许多称为覆盖块(overlay)的片段。覆盖块0首先运行,结束时他将调用另一个覆盖块。虽然覆盖块的交换是由OS完成的,但是必须先由程序员把程序先进行分割,这是一个费时费力的工作,而且相当枯燥。人们必须找到更好的办法从根本上解决这个问题。不久人们找到了一个办法,这就是虚拟存储器(virtual memory).虚拟存储器的基本思想是程序,数据,堆栈的总的大小可以超过物理存储器的大小,操作系统把当前使用的部分保留在内存中,而把其他未被使用的部分保存在磁盘上。比如对一个16MB的程序和一个内存只有4MB的机器,操作系统通过选择,可以决定各个时刻将哪4M的内容保留在内存中,并在需要时在内存和磁盘间交换程序片段,这样就可以把这个16M的程序运行在一个只具有4M内存机器上了。而这个16M的程序在运行前不必由程序员进行分割。

    编辑本段相关概念

    ——地址范围、虚拟地址映射为物理地址 以及分页机制
    任何时候,计算机上都存在一个程序能够产生的地址集合,我们称之为地址范围。这个范围的大小由CPU的位数决定,例如一个32位的CPU,它的地址范围是0~0xFFFFFFFF (4G),而对于一个64位的CPU,它的地址范围为0~0xFFFFFFFFFFFFFFFF (16E).这个范围就是我们的程序能够产生的地址范围,我们把这个地址范围称为虚拟地址空间,该空间中的某一个地址我们称之为虚拟地址。与虚拟地址空间和虚拟地址相对应的则是物理地址空间和物理地址,大多数时候我们的系统所具备的物理地址空间只是虚拟地址空间的一个子集。这里举一个最简单的例子直观地说明这两者,对于一台内存为256M的32bit x86主机来说,它的虚拟地址空间范围是0~0xFFFFFFFF(4G),而物理地址空间范围是0x000000000~0x0FFFFFFF(256M)。
    在没有使用虚拟存储器的机器上,虚拟地址被直接送到内存总线上,使具有相同地址的物理存储器被读写;而在使用了虚拟存储器的情况下,虚拟地址不是被直接送到内存地址总线上,而是送到存储器管理单元MMU,把虚拟地址映射为物理地址。
    大多数使用虚拟存储器的系统都使用一种称为分页(paging)机制。虚拟地址空间划分成称为页(page)的单位,而相应的物理地址空间也被进行划分,单位是页帧(frame).页和页帧的大小必须相同。在这个例子中我们有一台可以生成32位地址的机器,它的虚拟地址范围从0~0xFFFFFFFF(4G),而这台机器只有256M的物理地址,因此他可以运行4G的程序,但该程序不能一次性调入内存运行。这台机器必须有一个达到可以存放4G程序的外部存储器(例如磁盘或是FLASH),以保证程序片段在需要时可以被调用。在这个例子中,页的大小为4K,页帧大小与页相同——这点是必须保证的,因为内存和外围存储器之间的传输总是以页为单位的。对应4G的虚拟地址和256M的物理存储器,他们分别包含了1M个页和64K个页帧。

    编辑本段功能

    1、将虚拟地址映射为物理地址

    现代的多用户多进程操作系统,需要MMU,才能达到每个用户进程都拥有自己独立的地址空间的目标。使用MMU,操作系统划分出一段地址区域,在这块地址区域中,每个进程看到的内容都不一定一样。例如MICROSOFTWINDOWS操作系统将地址范围4M-2G划分为用户地址空间,进程A在地址0X400000(4M)映射了可执行文件,进程B同样在地址0X400000(4M)映射了可执行文件,如果A进程读地址0X400000,读到的是A的可执行文件映射到RAM的内容,而进程B读取地址0X400000时,则读到的是B的可执行文件映射到RAM的内容。
    这就是MMU在当中进行地址转换所起的作用。

    2、提供硬件机制的内存访问授权

    多年以来,微处理器一直带有片上存储器管理单元(MMU),MMU能使单个软件线程工作于硬件保护地址空间。但是在许多商用实时操作系统中,即使系统中含有这些硬件也没采用MMU。
    应用程序的所有线程共享同一存储器空间时,任何一个线程将有意或无意地破坏其它线程的代码、数据或堆栈。异常线程甚至可能破坏内核代码或内部数据结构。例如线程中的指针错误就能轻易使整个系统崩溃,或至少导致系统工作异常。
    就安全性和可靠性而言,基于进程的实时操作系统(RTOS)的性能更为优越。为生成具有单独地址空间的进程,RTOS只需要生成一些基于RAM的数据结构并使MMU加强对这些数据结构的保护。基本思路是在每个关联转换中“接入”一组新的逻辑地址。MMU利用当前映射,将在指令调用或数据读写过程中使用的逻辑地址映射为存储器物理地址。MMU还标记对非法逻辑地址进行的访问,这些非法逻辑地址并没有映射到任何物理地址
    这些进程虽然增加了利用查询表访问存储器所固有的系统开销,但其实现的效益很高。在进程边界处,疏忽或错误操作将不会出现,用户接口线程中的缺陷并不会导致其它更关键线程的代码或数据遭到破坏。目前在可靠性和安全性要求很高的复杂嵌入式系统中,仍然存在采无存储器保护的操作系统的情况,这实在有些不可思议。
    采用MMU还有利于选择性地将页面映射或解映射到逻辑地址空间。物理存储器页面映射至逻辑空间,以保持当前进程的代码,其余页面则用于数据映射。类似地,物理存储器页面通过映射可保持进程的线程堆栈。RTOS可以在每个线程堆栈解映射之后,很容易地保留逻辑地址所对应的页面内容。这样,如果任何线程分配的堆栈发生溢出,将产生硬件存储器保护故障,内核将挂起该线程,而不使其破坏位于该地址空间中的其它重要存储器区,如另一线程堆栈。这不仅在线程之间,还在同一地址空间之间增加了存储器保护。
    存储器保护(包括这类堆栈溢出检测)在应用程序开发中通常非常有效。采用了存储器保护,程序错误将产生异常并能被立即检测,它由源代码进行跟踪。如果没有存储器保护,程序错误将导致一些细微的难以跟踪的故障。实际上,由于在扁平存储器模型中,RAM通常位于物理地址的零页面,因此甚至NULL指针引用的解除都无法检测到。

    编辑本段MMU和CPU

    1、X86系列的MMU

    INTEL出品的80386CPU或者更新的CPU中都集成有MMU. 可以提供32BIT共4G的地址空间.
    X86 MMU提供的寻址模式有4K/2M/4M的PAGE模式(根据不同的CPU,提供不同的能力),此处提供的是目前大部分操作系统使用的4K分页机制的描述,并且不提供ACCESS CHECK的部分。
    涉及的寄存器
    a) GDT
    b) LDT
    c) CR0
    d) CR3
    e) SEGMENT REGISTER
    虚拟地址到物理地址的转换步骤
    a) SEGMENT REGISTER作为GDT或者LDT的INDEX,取出对应的GDT/LDT ENTRY.
    注意: SEGMENT是无法取消的,即使是FLAT模式下也是如此. 说FLAT模式下不使用SEGMENT REGISTER是错误的. 任意的RAM寻址指令中均有DEFAULT的SEGMENT假定. 除非使用SEGMENT OVERRIDE PREFⅨ来改变当前寻址指令的SEGMENT,否则使用的就是DEFAULT SEGMENT.
    ENTRY格式
    typedef struct
    {
    UINT16 limit_0_15;
    UINT16 base_0_15;
    UINT8 base_16_23;
    UINT8 accessed : 1;
    UINT8 readable : 1;
    UINT8 conforming : 1;
    UINT8 code_data : 1;
    UINT8 app_system : 1;
    UINT8 dpl : 2;
    UINT8 present : 1;
    UINT8 limit_16_19 : 4;
    UINT8 unused : 1;
    UINT8 always_0 : 1;
    UINT8 seg_16_32 : 1;
    UINT8 granularity : 1;
    UINT8 base_24_31;
    } CODE_SEG_DESCRIPTOR,*PCODE_SEG_DESCRIPTOR;
    typedef struct
    {
    UINT16 limit_0_15;
    UINT16 base_0_15;
    UINT8 base_16_23;
    UINT8 accessed : 1;
    UINT8 writeable : 1;
    UINT8 expanddown : 1;
    UINT8 code_data : 1;
    UINT8 app_system : 1;
    UINT8 dpl : 2;
    UINT8 present : 1;
    UINT8 limit_16_19 : 4;
    UINT8 unused : 1;
    UINT8 always_0 : 1;
    UINT8 seg_16_32 : 1;
    UINT8 granularity : 1;
    UINT8 base_24_31;
    } DATA_SEG_DESCRIPTOR,*PDATA_SEG_DESCRIPTOR;
    共有4种ENTRY格式,此处提供的是CODE SEGMENT和DATA SEGMENT的ENTRY格式. FLAT模式下的ENTRY在base_0_15,base_16_23处为0,而limit_0_15,limit_16_19处为0xfffff. granularity处为1. 表名SEGMENT地址空间是从0到0XFFFFFFFF的4G的地址空间.
    b) 从SEGMENT处取出BASE ADDRESS 和LIMIT. 将要访问的ADDRESS首先进行ACCESS CHECK,是否超出SEGMENT的限制.
    c) 将要访问的ADDRESS+BASE ADDRESS,形成需要32BIT访问的虚拟地址. 该地址被解释成如下格式:
    typedef struct
    {
    UINT32 offset :12;
    UINT32 page_index :10;
    UINT32 pdbr_index :10;
    } VA,*LPVA;
    d) pdbr_index作为CR3的INDEX,获得到一个如下定义的数据结构
    typedef struct
    {
    UINT8 present :1;
    UINT8 writable :1;
    UINT8 supervisor :1;
    UINT8 writethrough:1;
    UINT8 cachedisable:1;
    UINT8 accessed :1;
    UINT8 reserved1 :1;
    UINT8 pagesize :1;
    UINT8 ignoreed :1;
    UINT8 avl :3;
    UINT8 ptadr_12_15 :4;
    UINT16 ptadr_16_31;
    }PDE,*LPPDE;
    e) 从中取出PAGE TABLE的地址. 并且使用page_index作为INDEX,得到如下数据结构
    typedef struct
    {
    UINT8 present :1;
    UINT8 writable :1;
    UINT8 supervisor :1;
    UINT8 writethrough:1;
    UINT8 cachedisable:1;
    UINT8 accessed :1;
    UINT8 dirty :1;
    UINT8 pta :1;
    UINT8 global :1;
    UINT8 avl :3;
    UINT8 ptadr_12_15 :4;
    UINT16 ptadr_16_31;
    }PTE,*LPPTE;
    f) 从PTE中获得PAGE的真正物理地址的BASE ADDRESS. 此BASE ADDRESS表名了物理地址的.高20位. 加上虚拟地址的offset就是物理地址所在了.

    2、ARM系列的MMU

    ARM出品的CPU,MMU作为一个协处理器存在。根据不同的系列有不同搭配。需要查询DATASHEET才可知道是否有MMU。如果有的话,一定是编号为15的协处理器。可以提供32BIT共4G的地址空间。
    ARM MMU提供的分页机制有1K/4K/64K 3种模式. 本文介绍的是目前操作系统通常使用的4K模式。
    涉及的寄存器,全部位于协处理器15.
    ARM cpu地址转换涉及三种地址:虚拟地址(VA,Virtual Address),变换后的虚拟地址(MVA,Modified Virtual Address),物理地址(PA,Physical Address)。没有启动MMU时,CPU核心、cache、MMU、外设等所有部件使用的都是物理地址。启动MMU后,CPU核心对外发出的是虚拟地址VA,VA被转换为MVA供cache、MMU使用,并再次被转换为PA,最后使用PA读取实际设备。
    ARM没有SEGMENT的寄存器,是真正的FLAT模式的CPU。给定一个ADDRESS,该地址可以被理解为如下数据结构:
    typedef struct
    {
    UINT32 offset :12;
    UINT32 page_index :8;
    UINT32 pdbr_index :12;
    } VA,*LPVA;
    从MMU寄存器2中取出BIT14-31,pdbr_index就是这个表的索引,每个入口为4BYTE大小,结构为
    typedef struct
    {
    UINT32 type :2; //always set to 01b
    UINT32 writebackcacheable:1;
    UINT32 writethroughcacheable:1;
    UINT32 ignore :1; //set to 1b always
    UINT32 domain :4;
    UINT32 reserved :1; //set 0
    UINT32 base_addr:22;
    } PDE,*LPPDE;
    获得的PDE地址,获得如下结构的ARRAY,用page_index作为索引,取出内容。
    typedef struct
    {
    UINT32 type :2; //always set to 11b
    UINT32 ignore :3; //set to 100b always
    UINT32 domain :4;
    UINT32 reserved :3; //set 0
    UINT32 base_addr:20;
    } PTE,*LPPTE;
    从PTE中获得的基地址和上offset,组成了物理地址.
    PDE/PTE中其他的BIT,用于访问控制。这边讲述的是一切正常,物理地址被正常组合出来的状况。
    ARM/X86 MMU使用上的差异
    ⒈X86始终是有SEGMENT的概念存在. 而ARM则没有此概念(没有SEGMENT REGISTER.).
    ⒉ARM有个DOMAIN的概念. 用于访问授权. 这是X86所没有的概念. 当通用OS尝试同时适用于此2者的CPU上,一般会抛弃DOMAIN的使用.

    作者:沙漏哟
    出处:计算机的未来在于连接
    本文版权归作者和博客园共有,欢迎转载,请留下原文链接
    微信随缘扩列,聊创业聊产品,偶尔搞搞技术
  • 相关阅读:
    js调用.net后台事件,和后台调用前台等方法以及js调用服务器控件的方法
    .net反编译工具reflector5.0 的介绍及使用
    box flex 弹性盒模型
    TransactionScope使用说明
    您的主机中的软件中止了一个已建立的连接。
    Android中Handler
    转载 JavaScript的24条实用建议
    repeater中的checkbox 的方法以及datalist中放了一个按牛!为什么我按该按牛时候不能触发ItemCommand事件的主要原因
    asp.net cookies用法
    常用的数据分页技术总结
  • 原文地址:https://www.cnblogs.com/yeahwell/p/5226044.html
Copyright © 2020-2023  润新知