本文主要聊聊Linux系统中,内存是如何通过页表进行管理的,以及虚拟地址和实际物理地址之间关系。
虚拟内存和物理内存关系。
探究如何拿到实际物理内存地址。
内存管理的知识非常庞大,比如 linux 三驾马车,CPU、IO、内存,内存可以说是这里面最复杂的,与 CPU 和 IO 的性能有着千丝万缕的关系,搞懂了内存问题,才可以真正的搞清楚很多 Linux 性能相关的问题。
一、Linux内存的管理原理
虚拟内存与物理内存
首先我们来先看看虚拟内存与物理内存,虚拟内存和物理内存的关系印证了一句名言,「操作系统中的任何问题都可以通过一个抽象的中间层来解决」,虚拟内存正是如此(在应用层和架构设计的时候也是如此)。


Linux 四级页表
PGD:Page Global Directory,页全局目录,是顶级页表。
PUD:Page Upper Directory,页上级目录,是第二级页表
PMD:Page Middle Derectory,页中间目录,是第三级页表。
PTE:Page Table Entry,页面表,最后一级页表,指向物理页面。

应用程序看到的只有虚拟内存,是看不到物理地址的。当然是有办法可以通过一些手段通过虚拟地址拿到物理地址。比如我们 malloc 一个 1M 的空间,返回了一个虚拟地址 0x7ffff7eec010,怎么知道这个虚拟地址对应的物理内存地址呢?
二、如何取得实际物理内存地址
int my_module_init(void) {unsigned long pa = 0;pgd_t *pgd = NULL;pud_t *pud = NULL;pmd_t *pmd = NULL;pte_t *pte = NULL;struct pid *p = NULL;struct task_struct *task_struct = NULL;p = find_vpid(pid); //根据pid号查找进程if (p == NULL) {printk("find_vpid() return null\n");return -1;}task_struct = pid_task(p, PIDTYPE_PID);//得到进程task_struct结构if (task_struct == NULL) {printk("pid_task() return null\n");return -1;}//下面开始通过task_struct中的mm获取pgdpgd = pgd_offset(task_struct->mm, va);printk("pgd_val = 0x%lx\n", pgd_val(*pgd));printk("pgd_index = %lu\n", pgd_index(va));if (pgd_none(*pgd)) {printk("Not mapped in pgd.\n");return -1;}pud = pud_offset(pgd, va); //获得pudprintk("pud_val = 0x%lx\n", pud_val(*pud));printk("pud_index = %lu\n", pud_index(va));if (pud_none(*pud)) {printk("Not mapped in pud.\n");return 0;}pmd = pmd_offset(pud, va); //获得pmdprintk("pmd_val = 0x%lx\n", pmd_val(*pmd));printk("pmd_index = %lu\n", pmd_index(va));if (pmd_none(*pmd)) {printk("Not mapped in pmd.\n");return 0;}pte = pte_offset_kernel(pmd, va); //获得pteprintk("pte_val = 0x%lx\n", pte_val(*pte));printk("pte_index = %lu\n", pte_index(va));if(pte_none(*pte)) {printk("Not mapped in pte.\n");return 0;}if (!pte_present(*pte)) {printk("pte not in RAM.\n");return 0;}unsigned long page_addr = 0;unsigned long page_offset = 0;page_addr = pte_val(*pte) & PAGE_MASK; //页面的物理地址page_addr &= 0x7fffffffffffffULL; //转为无符号数字page_offset = va & ~PAGE_MASK; //获取线性地址偏移量pa = page_addr | page_offset; //实际物理地址printk("page_addr=0x%lx,page_offset=0x%03lx",page_addr,page_offset);printk("virtual address 0x%lx in RAM Page is 0x%lx", va, pa);return 0;}
obj-m += my_mem.oall:make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modulesclean:make -C /lib/modules/$(shell uname -r)/build M=$(PWD) cleaninsmod:sudo insmod my_mem.kormmod:sudo rmmod my_mem.ko
三、总结
文章转载自码农的修炼之道,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




