Linux虚拟地址到物理地址

本文解析了在Linux系统中,特别是ARM架构下,用户态和内核态虚拟地址到物理地址的映射过程,重点介绍了pgd、pmd和pte的作用以及它们在地址转换中的计算方法,包括pte_offset_map和__va宏的用法。
摘要由CSDN通过智能技术生成

代码实现

static u32 user_va2_pa(struct mm_struct *mm, u32 address) {
	pgd_t *pgd;
	pmd_t *pmd;
	pte_t *ptep, pte;

	if(!mm)
	{
		return 0;
	}

	pgd = pgd_offset(mm, address);
	if (pgd && !(pgd_none(*pgd) || pgd_bad(*pgd))) {
			ptep = pte_offset_map((pmd_t *)pgd, address);
			if (ptep) {
    			pte = *ptep;
				if (pte_present(pte))
				{
					return pte & PAGE_MASK;
				}
			}
		}
	}

	return 0;
}

代码解析

        在内存管理单元控制下,主要分用户态映射和内核态映射。内核态虚拟地址从0xC0000000开始,且为线性映射,虚拟地址=物理地址+映射偏移。

        需要注意的是,页表存放的值为物理内存地址,可以被内核态引用,也会被硬件分页地址计算引用,区别在于内核态引用时,需要转换为虚拟地址访问。

        内核页表有四层,包括pgd->pud->pmd->pte。

        arm-linux使用两级页表:pgd->pmd->page,pgd指向一级页目录项。

        具体公式如下:

        pdg首地址 = mm->pgd,是一级页表(页目录,page dir)虚拟地址,保存在线程的mm结构内

        pgd变量 = pgd首地址 + 虚拟地址高12位,pgd变量指向的内存保存二级页表位置。

        pmd变量 = pud变量 = pgd变量

static inline pte_t *pte_offset_kernel(pmd_t *pmd, unsigned long address)
{
	return (pte_t *)pmd_page_vaddr(*pmd) + pte_index(address);
}
#define __va(x)			((void *)__phys_to_virt((phys_addr_t)(x)))
static inline pte_t *pmd_page_vaddr(pmd_t pmd)
{
	return __va(pmd_val(pmd) & PHYS_MASK & (s32)PAGE_MASK);
}

        pte_offset_map(实际为pte_offset_kernel),用于从pmd(pgd)继续索引二级页表,当对pmd变量进行指针操作时,需要注意获取的内存值是二级页表的物理地址,因此要启用如下的宏pmd_page_vaddr将地址转换回虚拟地址后,才能正确访问。

        ptep是二级页表项指针,这里区分下术语:

        1. 页表,指整个表内存,也可以认为是页表首地址
        2. 页表项,项这个字指页表内一个项,包含两个要素:页表项所在地址和页表项保存的下一级页面地址

        ptep=转换虚拟地址(*pmd) + 目标虚拟地址[20:12]位偏移,ptep(页表项指针)就是pte(页表项)也就是二级页表项的地址

        pte=*ptep,pte就是页表项内容,保存指向页面具体的物理地址。至此,用户地址向物理地址的寻址就完成了。如果需要在内核态下访问pte指向的页,还需要通过__va宏,将pte保存的页物理地址转换为软件可访问的虚拟地址。

页表参考文献

linux内存子系统 - arm 页表硬件实现 - 知乎

Linux的线程内存空间参考:Linux 内核线程及普通进程总结

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值