linux内存管理-虚拟地址转换成物理地址的过程

          我们知道一个虚拟地址要依次经过分段机制转为线性地址,再经过分页机制从线性地址

转换为物理地址。虚拟地址是由段标识符:段内偏移来表示。
linux为了便于管理,将所有段标识符都设置为0,这样段内偏移就相当于线性地址。
之前简单了介绍了分段,分页的原理,接下来通过代码实例进一步加深对地址转换过程的理解,下面的示例是以arm64 来说明的:

static unsigned long test_vaddr2paddr(unsigned long vaddr)
{
pgd_t *pgd;
pud_t *pud;
pmd_t *pmd;
pte_t *pte;
unsigned long paddr = 0;
unsigned long page_addr = 0;
unsigned long page_offset = 0;


/*init_mm.pgd 是内核空间的页目录,整个内核空间使用此pgd;
每个用户进程都有自己的pgd,struct task_struct->struct mm_struct->pgd
因为地址是在内核空间分配的,因此使用内核的pgd;*/
//这一步是跟进pgd首地址,跟线性地址里面包含的偏移量得到下一级页表pud的物理地址,
pgd = pgd_offset(&init_mm,vaddr);
    
printk("pgd_val = 0x%lx pgd addr:0x%lx\n", (unsigned long int)pgd_val(*pgd),(unsigned long int)pgd_val(pgd));
printk("init_mm pgd val:0x%lx,pgd addr:0x%lx\n",(unsigned long )pgd_val(*(init_mm.pgd)),pgd_val((init_mm.pgd)));
printk("pgd_index = %ld\n", pgd_index(vaddr));
if (pgd_none(*pgd)) {
printk("not mapped in pgd\n");
return -1;


}
printk("\n");

//计算pmd页表地址
pud = pud_offset(pgd, vaddr);
printk("pud_val = 0x%lx \n", (unsigned long int)pud_val(*pud));
if (pud_none(*pud)) {
printk("not mapped in pud\n");
return -1;
}
printk("\n");
//计算pte
pmd = pmd_offset(pud, vaddr);
printk("pmd_val = 0x%lx\n", (unsigned long int)pmd_val(*pmd));
printk("pmd_index = %lu\n", pmd_index(vaddr));
if (pmd_none(*pmd)) {
printk("not mapped in pmd\n");
return -1;
}
printk("\n");


pte = pte_offset_kernel(pmd, vaddr);
printk("pte_val = 0x%lx\n", (unsigned long int)pte_val(*pte));
printk("pte_index = %lu\n", pte_index(vaddr));
if (pte_none(*pte)) {
printk("not mapped in pte\n");
return -1;
}

//先将pte值转换为struct page,然后在转换未物理地址
page_addr=page_to_phys(pte_page(*pte));
page_offset=pte_index(vaddr); //页内偏移量
paddr = page_addr | page_offset;


printk("page_addr = %lx, page_offset = %lx\n", page_addr, page_offset);
printk("vaddr = %lx, paddr = %lx\n", vaddr, paddr);


return paddr;


}


static int  test_addr_convet(void)
{
unsigned long vaddr = 0;
vaddr = (unsigned long)vmalloc(1000 * sizeof(char));
if (vaddr == 0) {
printk("vmalloc failed..\n");
return 0;
}


printk("vmalloc_vaddr=0x%lx\n", vaddr);
test_vaddr2paddr(vaddr);


vaddr = __get_free_page(GFP_KERNEL);
if (vaddr == 0) {
printk("__get_free_page failed..\n");
return 0;
}
printk("alloc page addr:0x%lx\n",vaddr);
test_vaddr2paddr(vaddr);


vaddr=(unsigned long)kmalloc(4096,GFP_KERNEL);
if(vaddr ==0)
{
printk(KERN_ERR "kmall fail\n");
return 0;
}
printk("kmalloc addr=0x%lx\n", vaddr);
test_vaddr2paddr(vaddr);
printk("\n\n");


return 0;
}

这段代码可以用于演示地址转换的详细过程,运行的结果如下(不同的平台运行结果会有差异,仅供参考):

[   76.354838] @0 vmalloc_vaddr=0xffffff8001d90000
[   76.358514] @0 pgd_val = 0x1aac06003 pgd addr:0xffffffc001a04000
[   76.364722] @0 init_mm pgd val:0x1aac06003,pgd addr:0xffffffc001a04000
[   76.373907] @0 pgd_index = 0
[   76.375932] @0 
[   76.377746] @0 pud_val = 0x1aac06003 
[   76.382005] @3 
[   76.382886] @3 pmd_val = 0x1a980f003
[   76.386454] @3 pmd_index = 14
[   76.389398] @3 
[   76.391282] @3 pte_val = 0x2e000017da44713
[   76.395260] @3 pte_index = 400
[   76.398246] @3 pte_val = 0x2e000017da44713
[   76.402447] @3 pte_index = 400
[   76.405392] @3 
[   76.407134] @3 page_addr = 17da44000, page_offset = 190
[   76.412404] @3 vaddr = ffffff8001d90000, paddr = 17da44190
[   76.417798] @3 alloc page addr:0xffffffc13da7e000
[   76.422562] @3 pgd_val = 0x7fffa003 pgd addr:0xffffffc001a04820
[   76.428371] @3 init_mm pgd val:0x1aac06003,pgd addr:0xffffffc001a04000
[   76.434942] @3 pgd_index = 260
[   76.437918] @3 
[   76.439652] @3 pud_val = 0x7fffa003 
[   76.443271] @3 
[   76.444964] @3 pmd_val = 0x1bcec2003
[   76.448503] @3 pmd_index = 493
[   76.451655] @3 
[   76.453285] @3 pte_val = 0x2c000017da7e713
[   76.457531] @3 pte_index = 126
[   76.460540] @3 pte_val = 0x2c000017da7e713
[   76.464483] @3 pte_index = 126
[   76.467519] @3 
[   76.469258] @3 page_addr = 17da7e000, page_offset = 7e
[   76.474514] @3 vaddr = ffffffc13da7e000, paddr = 17da7e07e
[   76.479885] @3 kmalloc addr=0xffffffc16424e000
[   76.484329] @3 pgd_val = 0x7fff9003 pgd addr:0xffffffc001a04828
[   76.490296] @3 init_mm pgd val:0x1aac06003,pgd addr:0xffffffc001a04000
[   76.496711] @3 pgd_index = 261
[   76.499723] @3 
[   76.501562] @3 pud_val = 0x7fff9003 
[   76.505077] @3 
[   76.506824] @3 pmd_val = 0x1bcd8e003
[   76.510450] @3 pmd_index = 289
[   76.513421] @3 
[   76.515091] @3 pte_val = 0x2c00001a424e713
[   76.519171] @3 pte_index = 78
[   76.522241] @3 pte_val = 0x2c00001a424e713
[   76.526243] @3 pte_index = 78
[   76.529204] @3 
[   76.531028] @3 page_addr = 1a424e000, page_offset = 4e
[   76.536062] @3 vaddr = ffffffc16424e000, paddr = 1a424e04e


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值