MMU是Memory Management Unit的缩写,中文名是内存管理单元,MMU是由ARM芯片中的cp15协处理器管理,它的作用是负责虚拟内存到物理内存的映射
要将虚拟内存映射为物理内存,就要需要构建一张映射表,那么如何来构建这张映射表将至关重要,目前,32位操作系统提供的虚拟存为4G,每个地址占16位4个字节,如果虚拟内存与物理内存要实现一一对应,那么这个张表的一条记录就要占用8个字节,整张表的大小就为32G,这肯定是不可取的;于是我们就采用另一种方法,我们将一个地址的前12为即2的12次方4k做为基地址,后20位作为偏移量,现在我们只需要将虚拟地址的前12位与物理地址的前12一一对应;这样每条记录8字节,整个表就变为32k,明显小了很多,那么我们在访问的时候就需要通过基地址找到所对应的物理地址然后加上偏移量即可(基地址 + 偏移量);
因此,假如我们需要将虚拟地址0x2ab45678映射到物理地址0x6ab45678,我们就可以先通过映射表找到基地址0x2ab所对应的物理地址为0x6ab00000,然后加上偏移量45678,就可以得到实际的物理地址为:0x6ab45678;
0x20000000 | 0x60000000 |
0x20100000 | 0x60100000 |
.... | ... |
0x2ab00000 | 0x6ab00000 |
因为我们在存映射表的时候就会有一个地址,所以我们可以将映射表的地址利用起来:假设映射表的地址为0x50000000,因此我们可以这样存:
0x6ab00000 |
.......... |
0x60100000 |
0x60000000 |
——>0x50000000 +(2ab * 4)
.......
——>0x500000000 +(201 * 4)
——>0x500000000 +(200 * 4)
0x50000000//映射表的地址,这样我们映射表存的地址就只有物理地址了,整个表的大小就只有16k了;
下面我们通过代码实现整个过程:
我们在物理地址0x6ab45678存个数据0x12345678,要是通过虚拟地址0x2ab45678也能访问得到;就说明映射成功,否则映射失败
1 /*将虚拟地址0x2ab12345映射到物理地址0x6ab12345*/ 2 int (*printf)(char *, ...) = 0xc3e114d8; 3 void enable_mmu(); 4 void init_table(unsigned long *addr); 5 6 int main() 7 { 8 //0x2ab00000 -> 0x6ab00000 9 unsigned long *virt = 0x2ab45678; 10 unsigned long *phys = 0x6ab45678; 11 *phys =0x12345678; 12 printf("phys %x\n", *phys); 13 enable_mmu(); 14 printf("virt %x\n", *virt); 15 16 return 0; 17 } 18 19 void enable_mmu() 20 { 21 /*构建表*/ 22 unsigned long addr = 0x50000000; 23 init_table(addr); 24 /*打开mmu*/ 25 unsigned long mmu = 0; 26 mmu = 1 | (1 << 1) | (1 << 3) | (1 << 8); 27 __asm__ __volatile__ ( 28 "mov r0, #3\n" 29 "MCR p15, 0, r0, c3, c0, 0\n"//设置为管理员 30 "MCR p15, 0, %0, c2, c0, 0\n"//设置表的地址 31 "MCR p15, 0, %1, c1, c0, 0\n"//开启mmu 32 : 33 : "r" (addr), "r" (mmu) 34 : 35 ); 36 37 } 38 39 void init_table(unsigned long *addr) 40 { 41 unsigned long va = 0; 42 unsigned long phys = 0; 43 44 //0x20000000 -> 0x60000000 45 //0x2ab00000 -> 0x6ab00000 46 for(va = 0x20000000; va < 0x30000000; va += 0x100000) { 47 phys = va + 0x40000000; 48 addr[va >> 20] = phys | 2; 49 } 50 //0x40000000-0x80000000 -> 0x40000000-0x80000000 51 for(va = 0x40000000; va < 0x80000000; va += 0x100000) { 52 phys = va; 53 addr[va >> 20] = phys | 2; 54 } 55 56 //0x10000000-0x14000000 -> 0x10000000-0x140000000 57 for(va = 0x10000000; va < 0x14000000; va += 0x100000) { 58 phys = va; 59 addr[va >> 20] = phys | 2; 60 } 61 62 }
在开发板运行结果如下: