内存管理单元编程实例【转】

本文涉及的产品
公网NAT网关,每月750个小时 15CU
简介:

转自:http://blog.chinaunix.net/uid-29091195-id-4020919.html

一、MMU初步了解
    概念:内存管理单元(memory management unit),简称MMU
    主要作用:负责虚拟地址到物理地址之间的转换
             提供硬件机制的内存访问权限检查
    
    发展由来:由于程序的大小逐步发展到大于内存的大小这一问题的出现
    导致了虚拟存储器的产生,虚拟存储器的基本思想是:数据、堆栈的总
    大小可以超越物理存储器(大于内存的程序在执行的时候只将其中需要
    运行的一部分调入内存,其他部分在外部存储器中)。由于虚拟存储器
    的出现导致cpu发出的虚拟地址不能够直接送至地址总线,这是就需要
    一个将虚拟地址转换物理地址的设备,它就是内存管理单元(MMU)。
    
    32位处理器最大可以虚拟出的地址范围是0~0xffffffff
    
二、工作特点
    1、没有启用MMU时候,cpu发出的地址是物理地址
    2、启用MMU后,cpu发出的是虚拟地址
    注:另一说法其实是cpu总发出虚拟地址,只不过是在没有进行空间映射
    时候,虚拟地址到物理地址的转换是一种线性转换的机制。
    
    虚拟地址到物理地址的转换方法一般有两种:
    其一、使用一个固定的函数式
    其二、建立一个表格进行映射。(页表由一个个条目组成)
    
    ARM cpu使用页表进行地址转换的过程:
    1)根据给定的虚拟地址找到一级页表中的条目
    2)如果此条目是段描述符,直接返回物理地址,转换结束
    3)否则此条目是二级页表,继续在二级表中查找下一条目
    4)如果此条目是页描述符,则返回物理地址,转换结束
    5)其他的情况,则转换出错
    
    注:有上述的过程不难知道一般的ARM处理器最多用到二级页表。
    
三、TLB了解
    概念:转译查找缓存(translation lookaside buffer)
    作用:解决MMU每次转换都需查表,降低了cpu性能的问题
    工作过程:当cpu发出一个虚拟地址的时候,MMU会首先访问TLB,如果
    在TLB中有这一虚拟地址的描述符,则直接使用该描述符进行地址转换
    。否则MMU访问页表找到对应的描述符在进行地址转换和权限检查,同时
    使用一定的算法将这一描述填入TLB中。
    
四、Cache了解
    概念:在主存和cpu的寄存器之间的告诉小容量存储器,简称高速缓冲存
    储器,也有的将其叫做快取
    作用:将正在执行的指令地址附近的一部分指令或者数据从主存调入这
    个存储器,供cpu在一段时间内使用,从而提高程序运行的速度。
    
    一般离cpu越近的cache速度越高,但是其容量一般比较小(按一级、二级
    cache来说,一级更高速,二级容量更大一些)
    
    cache有指令快取(ICache)和数据快取(DCache)
    
五、mmu编程实例:(实验平台mini2440)
    下面的实验是将GPB的地址0x56000000映射到0xA0000000,然后再对虚拟地址
    进行操作,点亮led的实例,实验目的在于学习mmu的页表建立,以及的段址映射
    的方法。
    
    1、启动代码(start.S)
    .text
    .global _start
    _start:
        ldr sp, =0x4096            @以下是c程序,运行前需要设置栈指针
        bl  disable_watch_dog
        bl  memsetup
        bl  copy_2th_to_sdram
        bl  create_page_table
        bl  mmu_init
        ldr sp, =0xB4000000
        ldr pc, =0xB0000000     @这个地址很重要,他必须是第二阶段代码的重载地址(运行地址)
    halt:
        bl  halt
        
    2、初始化事件(init.c)
    

点击(此处)折叠或打开

  1. /*
  2.  * init.c: 进行一些初始化,在Steppingstone中运行
  3.  * 它和head.S同属第一部分程序,此时MMU未开启,使用物理地址
  4.  */ 
  5. /* WATCHDOG寄存器 */
  6. #define WTCON (*(volatile unsigned long *)0x53000000)
  7. /* 存储控制器的寄存器起始地址 */
  8. #define MEM_CTL_BASE 0x48000000
  9. /*
  10.  * 关闭WATCHDOG,否则CPU会不断重启
  11.  */
  12. void disable_watch_dog(void)
  13. {
  14.     WTCON = 0; // 关闭WATCHDOG很简单,往这个寄存器写0即可
  15. }
  16. /*
  17.  * 设置存储控制器以使用SDRAM
  18.  */
  19. void memsetup(void)
  20. {
  21.     /* SDRAM 13个寄存器的值 */
  22.     unsigned long const mem_cfg_val[]={ 0x22011110, //BWSCON
  23.                                             0x00000700, //BANKCON0
  24.                                             0x00000700, //BANKCON1
  25.                                             0x00000700, //BANKCON2
  26.                                             0x00000700, //BANKCON3 
  27.                                             0x00000700, //BANKCON4
  28.                                             0x00000700, //BANKCON5
  29.                                             0x00018005, //BANKCON6
  30.                                             0x00018005, //BANKCON7
  31.                                             0x008C07A3, //REFRESH
  32.                                             0x000000B1, //BANKSIZE
  33.                                             0x00000030, //MRSRB6
  34.                                             0x00000030, //MRSRB7
  35.                                     };
  36.     int i = 0;
  37.     volatile unsigned long *p = (volatile unsigned long *)MEM_CTL_BASE;
  38.     for(; i < 13; i++)
  39.         p[i] = mem_cfg_val[i];
  40. }
  41. /*
  42.  * 将第二部分代码复制到SDRAM
  43.  */
  44. void copy_2th_to_sdram(void)
  45. {
  46.     unsigned int *pdwSrc = (unsigned int *)2048;       //这个地址必须是第二阶段代码的加载地址
  47.     unsigned int *pdwDest = (unsigned int *)0x30000000;
  48.     
  49.     while (pdwSrc < (unsigned int *)4096)
  50.     {
  51.         *pdwDest = *pdwSrc;
  52.         pdwDest++;
  53.         pdwSrc++;
  54.     }
  55. }
  56. /*
  57.  * 设置页表
  58.  */
  59. void create_page_table(void)
  60. {
  61. /* 
  62.  * 用于段描述符的一些宏定义
  63.  */ 
  64. #define MMU_FULL_ACCESS (3 << 10) /* 访问权限 */
  65. #define MMU_DOMAIN (0 << 5) /* 属于哪个域 */
  66. #define MMU_SPECIAL (1 << 4) /* 必须是1 */
  67. #define MMU_CACHEABLE (1 << 3) /* cacheable */
  68. #define MMU_BUFFERABLE (1 << 2) /* bufferable */
  69. #define MMU_SECTION (2) /* 表示这是段描述符 */
  70. #define MMU_SECDESC (MMU_FULL_ACCESS | MMU_DOMAIN | MMU_SPECIAL | \
  71.                              MMU_SECTION)
  72. #define MMU_SECDESC_WB (MMU_FULL_ACCESS | MMU_DOMAIN | MMU_SPECIAL | \
  73.                              MMU_CACHEABLE | MMU_BUFFERABLE | MMU_SECTION)
  74. #define MMU_SECTION_SIZE 0x00100000
  75.     unsigned long virtuladdr, physicaladdr;
  76.     unsigned long *mmu_tlb_base = (unsigned long *)0x30000000;
  77.     
  78.     /*
  79.      * Steppingstone的起始物理地址为0,第一部分程序的起始运行地址也是0,
  80.      * 为了在开启MMU后仍能运行第一部分的程序,
  81.      * 将0~1M的虚拟地址映射到同样的物理地址
  82.      */
  83.     virtuladdr = 0;
  84.     physicaladdr = 0;
  85.     *(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | \
  86.                                             MMU_SECDESC_WB;
  87.     /*
  88.      * 0x56000000是GPIO寄存器的起始物理地址,
  89.      * GPFCON和GPFDAT这两个寄存器的物理地址0x56000050、0x56000054,
  90.      * 为了在第二部分程序中能以地址0xA0000050、0xA0000054来操作GPFCON、GPFDAT,
  91.      * 把从0xA0000000开始的1M虚拟地址空间映射到从0x56000000开始的1M物理地址空间
  92.      */
  93.     virtuladdr = 0xA0000000;
  94.     physicaladdr = 0x56000000;
  95.     *(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | \
  96.                                             MMU_SECDESC;
  97.     /*
  98.      * SDRAM的物理地址范围是0x30000000~0x33FFFFFF,
  99.      * 将虚拟地址0xB0000000~0xB3FFFFFF映射到物理地址0x30000000~0x33FFFFFF上,
  100.      * 总共64M,涉及64个段描述符
  101.      */
  102.     virtuladdr = 0xB0000000;
  103.     physicaladdr = 0x30000000;
  104.     while (virtuladdr < 0xB4000000)
  105.     {
  106.         *(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | \
  107.                                                 MMU_SECDESC_WB;
  108.         virtuladdr += 0x100000;
  109.         physicaladdr += 0x100000;
  110.     }
  111. }
  112. /*
  113.  * 启动MMU
  114.  */
  115. void mmu_init(void)
  116. {
  117.     unsigned long ttb = 0x30000000;
  118. // ARM休系架构与编程
  119. // 嵌入汇编:LINUX内核完全注释
  120. __asm__(
  121.     "mov r0, #0\n"
  122.     "mcr p15, 0, r0, c7, c7, 0\n" /* 使无效ICaches和DCaches */
  123.     
  124.     "mcr p15, 0, r0, c7, c10, 4\n" /* drain write buffer on v4 */
  125.     "mcr p15, 0, r0, c8, c7, 0\n" /* 使无效指令、数据TLB */
  126.     
  127.     "mov r4, %0\n" /* r4 = 页表基址 */
  128.     "mcr p15, 0, r4, c2, c0, 0\n" /* 设置页表基址寄存器 */
  129.     
  130.     "mvn r0, #0\n" 
  131.     "mcr p15, 0, r0, c3, c0, 0\n" /* 域访问控制寄存器设为0xFFFFFFFF,
  132.                                          * 不进行权限检查 
  133.                                          */ 
  134.     /* 
  135.      * 对于控制寄存器,先读出其值,在这基础上修改感兴趣的位,
  136.      * 然后再写入
  137.      */
  138.     "mrc p15, 0, r0, c1, c0, 0\n" /* 读出控制寄存器的值 */
  139.     
  140.     /* 控制寄存器的低16位含义为:.RVI ..RS B... .CAM
  141.      * R : 表示换出Cache中的条目时使用的算法,
  142.      * 0 = Random replacement;1 = Round robin replacement
  143.      * V : 表示异常向量表所在的位置,
  144.      * 0 = Low addresses = 0x00000000;1 = High addresses = 0xFFFF0000
  145.      * I : 0 = 关闭ICaches;1 = 开启ICaches
  146.      * R、S : 用来与页表中的描述符一起确定内存的访问权限
  147.      * B : 0 = CPU为小字节序;1 = CPU为大字节序
  148.      * C : 0 = 关闭DCaches;1 = 开启DCaches
  149.      * A : 0 = 数据访问时不进行地址对齐检查;1 = 数据访问时进行地址对齐检查
  150.      * M : 0 = 关闭MMU;1 = 开启MMU
  151.      */
  152.     
  153.     /* 
  154.      * 先清除不需要的位,往下若需要则重新设置它们 
  155.      */
  156.                                         /* .RVI ..RS B... .CAM */ 
  157.     "bic r0, r0, #0x3000\n" /* ..11 .... .... .... 清除V、I位 */
  158.     "bic r0, r0, #0x0300\n" /* .... ..11 .... .... 清除R、S位 */
  159.     "bic r0, r0, #0x0087\n" /* .... .... 1... .111 清除B/C/A/M */
  160.     /*
  161.      * 设置需要的位
  162.      */
  163.     "orr r0, r0, #0x0002\n" /* .... .... .... ..1. 开启对齐检查 */
  164.     "orr r0, r0, #0x0004\n" /* .... .... .... .1.. 开启DCaches */
  165.     "orr r0, r0, #0x1000\n" /* ...1 .... .... .... 开启ICaches */
  166.     "orr r0, r0, #0x0001\n" /* .... .... .... ...1 使能MMU */
  167.     
  168.     "mcr p15, 0, r0, c1, c0, 0\n" /* 将修改的值写入控制寄存器 */
  169.     : /* 无输出 */
  170.     : "r" (ttb) );
  171. }

     注:这部分代码是linux应用手册中的实例程序,只是修改了第二阶段的代码拷贝的目的地址

    3、点亮led灯(led.c使用虚拟地址)
    

点击(此处)折叠或打开

  1. #define GPBCON                (*(volatile unsigned long *)0xA0000010) /* this is a virtual address */ 
  2. #define GPBDAT                (*(volatile unsigned long *)0xA0000014) /* this is a virtual address */ 
  3. #define GPBCON_5678_OUTPUT 0x1<<5*2 | 0x1<<6*2 | 0x1<<7*2 | 0x1<<8*2
  4. #define GPBDAT_5678_LOW        0x0<<5*1 | 0x0<<6*1 | 0x0<<7*1 | 0x0<<8*1
  5. int main(int argc, char *argv[])
  6. {
  7.     GPBCON &= ~(0x3<<5*2 | 0x3<<6*2 | 0x3<<7*2 | 0x3<<8*2);
  8.     GPBCON |= GPBCON_5678_OUTPUT;
  9.     GPBDAT &= ~(0x1<<5*1 | 0x1<<6*1 | 0x1<<7*1 | 0x1<<8*1);
  10.     GPBDAT |= GPBDAT_5678_LOW;    
  11.     GPBDAT |= 0x1<<6*1;
  12.     while (1);
  13.     
  14.     return 0;
  15. }


    4、工程链接脚本(mmu.lds)
    SECTIONS {
        first          0x00000000  :                    { start.o init.o }
        second     0xB0000000  :   AT(2048)    { led.o }
    }

    注:second段的加载地址必须与拷贝代码中的起始地址一致,否则拷贝后代码无法执行
          second段的运行地址(重载地址)必须与启动代码start.S中的跳转地址一致,否则
          pc指针无法正确的跳转到第二阶段执行点亮led灯的程序。               

    5、工程Makefile
    objs := start.o init.o led.o

    mmu.bin : (objs)armlinuxldTmmu.ldsommuelf(objs)arm−linux−ld−Tmmu.lds−ommuelf^
        arm-linux-objcopy -O binary -S mmu_elf @armlinuxobjdumpDmarmmmuelf>mmu.dis@arm−linux−objdump−D−marmmmuelf>mmu.dis<<@ $<

    clean:
        rm -f mmu.bin mmu_elf mmu.dis *.o        
    













本文转自张昺华-sky博客园博客,原文链接:http://www.cnblogs.com/sky-heaven/p/5684822.html,如需转载请自行联系原作者

相关文章
|
29天前
|
存储 分布式计算 网络协议
阿里云服务器内存型r7、r8a、r8y实例区别参考
在阿里云目前的活动中,属于内存型实例规格的云服务器有内存型r7、内存型r8a、内存型r8y这几个实例规格,相比于活动内的经济型e、通用算力型u1实例来说,这些实例规格等性能更强,与计算型和通用型相比,它的内存更大,因此这些内存型实例规格主要适用于数据库、中间件和数据分析与挖掘,Hadoop、Spark集群等场景,本文为大家介绍内存型r7、r8a、r8y实例区别及最新活动价格,以供参考。
阿里云服务器内存型r7、r8a、r8y实例区别参考
|
4月前
|
人工智能 Java 5G
常见的Java内存溢出情况和实例
常见的Java内存溢出情况和实例
|
7月前
|
存储 弹性计算 Linux
阿里云服务器实例规格CPU内存带宽系统盘等配置选择注意事项
在购买阿里云服务器时,实例规格、CPU、内存、带宽和系统盘等配置都是重要的,合理选择这些配置不仅能够更好地满足我们的需求,提高服务器的性能和稳定性。同时还能尽可能的节约购买成本,本文将对阿里云服务器实例规格CPU内存带宽系统盘等配置选项进行详细解释,并提供一些选择建议及注意事项,以供参考。
797 0
阿里云服务器实例规格CPU内存带宽系统盘等配置选择注意事项
|
19天前
|
存储 缓存 PHP
阿里云服务器实例、CPU内存、带宽、操作系统选择参考
对于使用阿里云服务器的用户来说,云服务器的选择和使用非常重要,如果实例、内存、CPU、带宽等配置选择错误,可能会影响到自己业务在云服务器上的计算性能及后期运营状况,本文为大家介绍一下阿里云服务器实例、CPU内存、带宽、操作系统的选择注意事项,以供参考。
阿里云服务器实例、CPU内存、带宽、操作系统选择参考
|
1月前
|
弹性计算 固态存储 调度
阿里云服务器部署配置选择全攻略,ECS实例规格、CPU内存配置
阿里云服务器部署配置选择全攻略,ECS实例规格、CPU内存配置,CPU内存、公网带宽和系统盘怎么选择?个人用户选择轻量应用服务器或ECS通用算力型u1云服务器,企业用户选择ECS计算型c7、通用型g7云服务器,阿里云百科分享阿里云服务器配置选择方法
|
3月前
|
存储 缓存 资源调度
阿里云购买云服务器选择经济型、通用算力型、计算型、通用型、内存型实例参考
在我们通过阿里云的活动选购云服务器的时候会发现,相同配置的云服务器往往有多个不同的实例可选,而且价格差别也比较大,这会是因为不同实例规格的由于采用的处理器不同,底层架构也有所不同(例如X86 计算架构与Arm 计算架构),因此不同实例的云服务器其性能与适用场景是有所不同。本文为大家详细介绍阿里云的经济型、通用算力型、计算型、通用型和内存型实例的性能特点及适用场景,以供大家选择参考。
阿里云购买云服务器选择经济型、通用算力型、计算型、通用型、内存型实例参考
|
3月前
|
存储 机器学习/深度学习 人工智能
阿里云倚天云服务器计算型c8y、通用型g8y、内存型r8y实例性能介绍及活动价格参考
计算型c8y、通用型g8y、内存型r8y是阿里云推出的基于ARM架构的新一代阿里自研倚天实例,采用阿里自研倚天710处理器,基于最新一代CIPU架构,通过芯片快速路径加速手段实现计算、存储、网络性能的大幅提升。其中计算型c8y实例网站应用、各种类型和规模的企业级应用,通用型g8y实例适用于网站应用、各种类型和规模的企业级应用,内存型r8y实例适用于网站应用、各种类型和规模的企业级应用。本文为大家详细介绍倚天云服务器实例的主要性能和最新活动价格情况。
阿里云倚天云服务器计算型c8y、通用型g8y、内存型r8y实例性能介绍及活动价格参考
|
3月前
|
存储 弹性计算 安全
阿里云七代云服务器计算型c7、通用型g7、内存型r7实例性能介绍及活动价格参考
阿里云第7代云服务器主要实例规格包括计算型c7、通用型g7、内存型r7,其中计算型c7实例适用于计算密集型业务,如Web服务器Apache、Nginx等场景,通用型g7实例适用于通用业务,如网站应用、各类企业级应用、中间件,内存型r7实例适用于内存密集型业务,如数据库系统、缓存、搜索集群。本文为大家详细介绍七代云服务器实例的主要性能和最新活动价格情况。
阿里云七代云服务器计算型c7、通用型g7、内存型r7实例性能介绍及活动价格参考
|
4月前
|
存储 弹性计算 缓存
阿里云服务器经济型、通用算力型、计算型、通用型、内存型实例区别及选择参考
当我们在选购阿里云服务器会发现,相同配置的云服务器往往有多个不同的实例可选,而且价格差别也比较大,因此,阿里云经济型、通用算力型、计算型、通用型、内存型实例云服务器有何差别就是很多新手用户比较关心的问题了,下面小编来为大家简单介绍下它们之间的区别。
阿里云服务器经济型、通用算力型、计算型、通用型、内存型实例区别及选择参考
|
8月前
|
存储 缓存 分布式计算
阿里云服务器内存型r5、r6、r7、r8y实例对比,看看他们有什么区别
目前阿里云在售的内存型云服务器中,包含了第5代内存型r5、第6代内存型r6、第7代内存型r6和最新第八代内存型r8y等,有的新手用户并不清楚这四代产品之间的差别,下面以内存型r5、r6、r7、r8y实例在规格、CPU(核)、内存(G)、计算、存储、内存以及不同配置的指标数据等方面为大家做个对比,让大家了解一下他们之间的不同,以供参考和选择。
492 0
阿里云服务器内存型r5、r6、r7、r8y实例对比,看看他们有什么区别