引言
为什么linux会发展成这个样子,这当然是程序员对程序的要求决定的,
为了满足这些要求,提出了 进程地址空间抽象(1) , 其中 硬件上增加了新的模块MMU(2), 软件上根据MMU的使用手册(3)更新了系统
另外在 进程地址空间抽象 的基础上, 软件上更新了很多新的内存特性(4).
这里主要讲 新的模块MMU(2),且我们这里只看arm的mmu
ARM-MMU文档
科普文档
- MMU 是 soc 芯片里面的一个硬件单元
- 去arm官网去搜mmu,得到结果
- 其中有一个 介绍性文档 : LearnTheArchitecture-MemoryManagement-101811_0100_00_en.pdf
专业文档
- 去http://infocenter.arm.com搜mmu,得到一个 RF 和 很多TRM(MMU-40x,MMU-500,MMU600,MMU-600AE)
- arm-mmu 的 RF : IHI0062D_c_system_mmu_architecture_specification.pdf
- arm-mmu 的TRM : corelink_mmu500_memory_management_unit_r2p4_technical_reference_manual_DDI0517F_en.pdf
MMU 与 内存管理的关系
为了 实现 进程地址空间抽象 , 在 硬件内存管理这方面由MMU负责
硬件内存管理干了什么
硬件内存管理(MMU)描述了如何控制对 系统内存 的访问。
每当操作系统或应用程序访问内存时,硬件都会执行内存管理。
硬件内存管理是一种将内存区域动态分配给应用程序的方法。 // 这个动态分配是什么意思
硬件内存管理的行为导致的效果
支持虚拟内存系统
软件只能看到虚拟地址,处理器会将其转换为物理地址。
这些物理地址被提供给内存系统,并指向内存中的实际物理位置。
// 动态分配???
It is optional which write allocation policies an implementation supports.
The allocate on write and no allocate on write cache policies indicate which allocation policy is preferred for a memory region, but it should not be relied on that the memory system implements that policy.
实现支持哪些写分配策略是可选的。
写时分配和不写时分配缓存策略指示内存区域首选哪个分配策略,但不应依赖于内存系统实现该策略。
MMU工作流程(三种描述方式)
- MMU工作流程文字描述
虚拟地址和物理地址的转换 总览
映射转换(翻译) 的过程
所需成员:
cpu mmu mmu中的TLBs mmu中的TableWalkUnit cache 主存中的转换表(页表) 主存中其他部分
1.取指令,译码
2.执行load指令(读一个内存地址中的值(value)到r0,内存地址为虚拟地址 va)
2.1 获取 物理地址 pa
2.1.1 从 TLBs 中 获取 pa ,如果获取不到则执行2.1.2
2.1.2 mmu中的TableWalkUnit 从 主存中的转换表(页表) 中获取 pa
2.2 根据物理地址 pa 获取 value
2.2.1 从 cache 中获取 value,如果获取不到则执行2.2.2
2.2.2 从 主存中其他部分 中获取 value.
主内存 中的 转换表 填充了 映射转换
映射转换
虚拟地址通过映射转换为物理地址。
虚拟地址和物理地址之间的映射存储在转换表(有时称为页表)
转换表:
转换表位于内存中,并由软件(通常是OS或管理程序)进行管理。
转换表不是静态的,并且可以根据软件需求的变化来更新这些表。
这将更改虚拟地址和物理地址之间的映射。
- MMU工作流程图解
在ARM系统中,在cache中使用虚拟地址在缓存中查找数据或指令是存在的,并利用 虚索引实Tag技术 实现
本文中没有描述在cache中用虚拟地址索引数据/指令这种情况
本文中只描述了在cache中用物理地址索引数据/指令
- MMU工作流程伪代码描述
/*
level-fetch:
zhanweifu
first-level-fetch // 获取第1级页表项的函数
second-level-fetch // 获取第2级页表项的函数
third-level-fetch // 获取第3级页表项的函数
fourth-level-fetch // 获取第4级页表项的函数
zhanweifu2
*/
void walktable(int vma,int *pa){
level-fetch = zhanweifu;
do{
level-fetch = next-level-fetch(level-fetch);
if (level-fetch == zhanweifu2){
throw execption;
return;
}
table-item=level-fetch(vma);
is-permission=do-permission-check(current-state,Attributes-of-table-item(table-item));
if(!is-permission){
throw execption;
return;
}
is-data-block-index = do-data-block-index-check(Attributes-of-table-item(table-item));
}while(!is-data-block-index);
pa = calc-pa(addr-base-of-table-item(table-item),vma);
return ;
}
int get_value(int pa){
is_cache_miss = get_value_from_cache(pa,&value);
if (is_cache_miss){
get_value_from_ddr(pa,&value);
}
return value;
}
int get_pa(int vma){
is_tlb_miss = tlb(vma,&pa)
if (is_tlb_miss){
try {
walktable(vma,&pa);
}catch (Exception XXX){
//TODO
}
}
return pa;
}
int mmu(int vma){
pa = get_pa(vma);
value = get_value(pa);
return value;
}
void cpu(void){ // main
try {
value = mmu(vma);
}catch (Exception alignment fault){
//TODO
}catch (Exception translation fault){
//TODO
}catch (Exception domain fault){
//TODO
}catch (Exception permission fault){
//TODO
}
}