MMU地址空间映射
虚拟地址-物理地址
虚拟地址,和芯片位数相关,对于32位芯片,一个指针的大小为4字节,寻址范围为【0x0000,0000 ~ 0xFFFF,FFFF】,寻址范围为4G,也就是说最多能够访问到4G的内存空间。但是实际情况下缺很尴尬,在早期,内存的真实物理容量都很小,通常只有几百兆,在这种情况下,如何让用户拥有4G的寻址空间呢?虽然这是一个假象。可以通过内存映射的方式,为每一个进程都营造出可以访问到4G内存空间的假象。 为什么要使用虚拟地址呢?在单片机上,没有MMU单元,我们都是直接访问的内存物理地址,这样会存在一些问题:
- 所有程序的资源都是共享的,如果有一个程序崩溃,会造成整个系统崩溃。
- 用户可以违法使用非法地址,来破坏系统,在有MPU的单片机上,可以做一些内存保护操作
- 所有程序都必须在不同的地址上运行,这在编译链接时就必须确定好,如果你的操作系统有成千上万的用户,那你还要为他们分配不同的地址,那工作量简直惨不忍睹,我们希望每个应用程序都可以从0开始链接,而且各自的资源相互独立,不能相互访问,只能通过操作系统提供的接口来提供IPC进程间通信的接口
基于如上,我们需要一套机制来实现内存映射机制,在ARM9上引入了MMU,这是实现内存映射的基本硬件单元。
MMU
MMU为内存管理单元,他提供2个功能可以帮助我们实现内存映射
当我们开启MMU后,CPU读取虚拟地址后,经过MMU单元,会硬件自动转换成物理地址,最后访问内存。注意,这是硬件自动完成的,你只需要设置好TTB的基地址就可以了。 映射流程:
- cpu获取虚拟地址VA
- 读取CP15中的C13将不同进程的相同虚拟地址转换成MVA,修正后的虚拟地址
- 读取CP15寄存器的C2寄存器,得到TTB的基地址,注意TTB是存放在物理地址中,在编译链接时确定。
- 通过查找页表,最后得到真实的物理地址
前提: 在映射之前,我们首先要做好映射工作,不然MMU根本无法进行地址转换
L1级映射
前提: 我们首先要确定我们要怎么映射。 比如说,我的物理地址是从0x5000,0000开始 我们想把虚拟地址4G空间里的高端内存1G空间规定为内核空间,低端3G空间作为用户空间。 我们先进行内核空间映射,我们想把虚拟地址0xC000,00000xFFFF,FFFF,映射到物理地址0x5000,00000x8FFF,FFFF 我们在编译链接时,就指定内核空间的代码和数据段到0xC000,0000开始
映射规则分为两种:
L1级映射为分段映射,映射规则很简单,把4G空间分为4096个段,每一个段大小为1M。 我们定义页表为:
int page_table[4096];
MVA = 0xC000,0000
index_L1 = MVA >> 20; 得到一级页表描述索引 index_L1 = 0xC00 进而得到一级页表描述地址为
PGD = page_table[index_L1];
PGD结构
低两位为[1:0],表示直接段映射 映射的真实物理地址为:
Phy_addr = base + offset = ((PGD>>20)<<20) + MVA[19:0]
物理地址为0x5000,0000,右移20位得到0x500;
AP,domain,C,B属性不设置,最终得到一级描述符位:0x12
pgd = ((0x500>>20)<<20)+0x12 = 0x5000,0012
我们将pgd的值写入到page_table[0xC00]里去
若低两位为[0:1],表示还有二级映射,也叫粗页表,PGD的高22位[31:10]表示二级页表的基地址
PTE_addr = ((PGD>>10)<<10);
L2级映射
二级页表为256个页,一般二级映射的单元为小页,大小为4K, 我们设置二级页表为:
page_table_L2 = PTE_addr
PTE_addr指向一个page_table_L2
index_L2 = MVA[19:12];
得到二级页表描述地址为
PTE = page_table_L2[index_L2];
PTE结构
低两位一般为[1:XN],表示页面大小为small page为4K 高20位表示base addr 最中的物理地址为:
base = (PTE>>20)<<20
Phy_addr = base + offset = ((PTE>>20)<<20) + MVA[11:0]
要实现0xC000,0000 到 0x5000,0000的映射,那么
pte = (0x5000,0000>>20)<<20 + 0x12 = 0x5000,0012 我们将pte的值写入到page_table_L2[index_L2]里去
映射流程
|