Chinaunix首页 | 论坛 | 博客
  • 博客访问: 185821
  • 博文数量: 41
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 131
  • 用 户 组: 普通用户
  • 注册时间: 2013-09-03 20:39
文章分类

全部博文(41)

文章存档

2019年(2)

2018年(1)

2014年(19)

2013年(19)

分类: LINUX

2019-05-30 23:55:06

简介

基于kernel 4.0分析create_mapping()函数,此函数用于创建内存页表,函数定义如下: 

不考虑LPAE情况,代码忽略。 
1.首先判断虚拟地址在用户区域,并且不是中断向量表(中断向量表可以在虚拟地址0开始的地方)。 
2.然后判断内存类型为IO和ROM类型的不允许映射在低端内存或高于VMALLOC_END区域,个人理解应该只能映射在vmalloc区域。

  1. type = &mem_types[md->type]; 获取所映射内存区间的属性。
  2. 映射区间不为段映射且不是1M对齐,则返回。 
    以上暂不关注

分析映射代码: 
typedef unsigned long pte_t; 
typedef unsigned long pmd_t; 
typedef unsigned long pgd_t[2]; 
typedef unsigned long pgprot_t;

pgd_t pgd; 

pgd = pgd_offset_k(addr); addr 是虚拟地址 
找到addr这个虚拟地址在内核页表中的位置,PGDIR_SHIFT等于21(使一级页表项8字节对齐),内核进程pgd的起始地址在0xC0004000,所以(init_mm)->pgd + ((addr) >> 21) = (pgd_t 
)0xc0004000 + (addr >> 21)。如果addr值为0xC0000000,它右移21位值为0x600,但是(pgd_t )0xc0004000 + 0x600 = 0xc0007000,是因为前面有强制类型转换(pgd_t ),而这个结构的定义是两个ulong,所以(pgd_t )0xc0004000 + 0x600 =(pgd_t )0xc0004000 + 0x600*8 = 0xc0007000。 
仿真结果如下图:vexpress_defconfig板子 

end = addr + length; end指的是要映射的虚拟地址的结尾,它的值为0xc0000000 + 0x1100000(17M) = 0xC1100000; 
要映射的虚拟地址起始位置0xC0000000,结束地址0xC1100000,一共17M,映射的物理地址是0x60000000。 

此时的pgd=0xc0007000,end=0xc1100000,addr=0xc0000000。 
第一次循环:next = 0xc0200000 
unsigned long next = pgd_addr_end(addr, end); 获取addr后下一个2M的虚拟起始地址,保证不超过end,如果超过end,则返end。 

这个宏定义同时还保证了2M对齐。

alloc_init_pud(pgd, addr, next, phys, type)

phys += next - addr; 物理地址增加到已映射结束的位置 
addr = next; 虚拟地址增加到已映射结束的位置 
这个循环周而复始直到映射完。 

pgd=0xc0007000,addr=0xc0000000,end=0xc0200000,phys=0x60000000 
pud 和pgd是同样的类型,pud=0xc0007000, 
进入循环: 
next = pud_addr_end(addr, end);获取addr后下一个2M的虚拟起始地址,不过end就是addr后的2M位置,所以next=0xc0200000。 
pud和pgd一样都是2M循环。

alloc_init_pmd(pud, addr, next, phys, type)


这里有对一级页表的初始化__map_init_section(pmd, addr, next, phys, type); 
也有对二级页表的初始化alloc_init_pte(pmd, addr, next, __phys_to_pfn(phys), type);

pud=0xc0007000,addr=0xc0000000,end=0xc0200000,phys=0x60000000 

pmd_t *pmd = pmd_offset(pud, addr);转化成long型指针 
pmd = 0xc0007000 
next = pmd_addr_end(addr, end); 
next = 0xc0200000

段映射 __map_init_section(pmd, addr, next, phys, type)

如果符合段映射则进入__map_init_section(pmd, addr, next, phys, type); 
pmd = 0xc0007000,addr=0xc0000000,next = 0xc0200000,phys=0x60000000 

do 循环里运行了2次,每次addr加1M,2次后addr==end。 
一级页表里的内容是*pmd = __pmd(phys | type->prot_sect); 0x6021140e 

二级页表映射 alloc_init_pte(pmd, addr, next, __phys_to_pfn(phys), type)


pte指向一页的起始地址 
arch/arm/mm/proc-v7-2level.S 
r0:页表项地址 
r1:页表属性|物理地址偏移(Linux 模拟的表项) 
将页表属性r1配置给页表项r0 


写入HW页表内容,HW页表位于软件页表+2048字节处 
arch/arm/include/asmpgtable-2level.h 

这张图说明了2级页表的布局

阅读(8574) | 评论(0) | 转发(0) |
0

上一篇:alloc_vmap_area 函数解析

下一篇:没有了

给主人留下些什么吧!~~