Linux内核C程序逻辑地址转物理地址报错及段错误修复求助
修复后的完整代码
#include <linux/mm.h> #include <linux/sched.h> #include <linux/pid.h> #include <linux/highmem.h> static unsigned long vaddr2paddr(struct seq_file *m, unsigned long vaddr, int pid) { pte_t *pte_tmp = NULL; pmd_t *pmd_tmp = NULL; pud_t *pud_tmp = NULL; p4d_t *p4d_tmp = NULL; pgd_t *pgd_tmp = NULL; struct task_struct *pcb_tmp = NULL; struct pid *pid_struct = NULL; unsigned long paddr = 0; unsigned long pfn; printk(KERN_INFO "in vaddr2paddr try to find the task %d .\n", pid); pid_struct = find_get_pid(pid); if (!pid_struct) { printk(KERN_INFO "Invalid pid %d .\n", pid); return 0; } pcb_tmp = pid_task(pid_struct, PIDTYPE_PID); if (!pcb_tmp) { printk(KERN_INFO "Can't find the task %d .\n", pid); put_pid(pid_struct); return 0; } /* 排除内核线程,内核线程没有用户地址空间 */ if (!pcb_tmp->mm) { printk(KERN_INFO "Task %d is kernel thread, no user mm .\n", pid); put_pid(pid_struct); return 0; } /* 遍历页表前持读锁,防止页表被并发修改 */ mmap_read_lock(pcb_tmp->mm); pgd_tmp = pgd_offset(pcb_tmp->mm, vaddr); if (pgd_none(*pgd_tmp) || pgd_bad(*pgd_tmp)) { printk("not mapped in pgd\n"); goto out_unlock; } p4d_tmp = p4d_offset(pgd_tmp, vaddr); if (p4d_none(*p4d_tmp) || p4d_bad(*p4d_tmp)) { printk("not mapped in p4d\n"); goto out_unlock; } pud_tmp = pud_offset(p4d_tmp, vaddr); if (pud_none(*pud_tmp) || pud_bad(*pud_tmp)) { printk("not mapped in pud\n"); goto out_unlock; } pmd_tmp = pmd_offset(pud_tmp, vaddr); if (pmd_none(*pmd_tmp) || pmd_bad(*pmd_tmp)) { printk("not mapped in pmd\n"); goto out_unlock; } /* 用户地址空间pte用pte_offset_map映射后访问,不能用内核专用的pte_offset_kernel */ pte_tmp = pte_offset_map(pmd_tmp, vaddr); if (pte_none(*pte_tmp) || !pte_present(*pte_tmp)) { printk("not mapped in pte or paged out\n"); goto out_unmap; } /* 计算物理地址:页帧号左移页大小位 + 页内偏移 */ pfn = pte_pfn(*pte_tmp); paddr = (pfn << PAGE_SHIFT) | (vaddr & ~PAGE_MASK); out_unmap: pte_unmap(pte_tmp); out_unlock: mmap_read_unlock(pcb_tmp->mm); put_pid(pid_struct); return paddr; }
核心修改点
- 补全P4D页表层遍历逻辑,匹配新版内核五级页表接口要求,解决编译类型不匹配错误
- 增加pid有效性校验、pid引用计数释放逻辑,避免无效pid访问和资源泄漏
- 增加内核线程判断,内核线程无用户地址空间,直接返回避免空指针访问
- 新增
mmap_lock读锁保护,遍历页表过程中禁止系统修改/回收页表,解决段错误问题 - 替换内核专用的
pte_offset_kernel为用户地址空间适用的pte_offset_map,访问结束后调用pte_unmap释放映射 - 增加各层页表项的bad校验,避免访问损坏的页表项
- 补充物理地址计算逻辑,通过页帧号加页内偏移得到最终物理地址
- 统一错误返回值为0(合法物理地址不可能为0),避免类型转换异常
内容的提问来源于stack exchange,提问作者Su.
相关产品推荐
相关产品推荐

