• 用户虚拟地址转化成物理地址,物理地址转换成内核虚拟地址,内核虚拟地址转换成物理地址,虚拟地址和对应页的关系


    1. 用户虚拟地址转换成物理地址

    static void get_pgtable_macro(void)
    {
        printk("PAGE_OFFSET = 0x%lx\n", PAGE_OFFSET);
        printk("PGDIR_SHIFT = %d\n", PGDIR_SHIFT);
        printk("PUD_SHIFT = %d\n", PUD_SHIFT);
        printk("PMD_SHIFT = %d\n", PMD_SHIFT);
        printk("PAGE_SHIFT = %d\n", PAGE_SHIFT);
        printk("PTRS_PER_PGD = %d\n", PTRS_PER_PGD);
        printk("PTRS_PER_PUD = %d\n", PTRS_PER_PUD);
        printk("PTRS_PER_PMD = %d\n", PTRS_PER_PMD);
        printk("PTRS_PER_PTE = %d\n", PTRS_PER_PTE);
        printk("PAGE_MASK = 0x%lx\n", PAGE_MASK);
    }
    static unsigned long 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;
        pgd = pgd_offset(current->mm, vaddr);
        printk("pgd_val = 0x%lx\n", pgd_val(*pgd));
        printk("pgd_index = %lu\n", pgd_index(vaddr));
        if (pgd_none(*pgd)) {
            printk("not mapped in pgd\n");
            return -1;
        }
        pud = pud_offset(pgd, vaddr);
        printk("pud_val = 0x%lx\n", pud_val(*pud));
        if (pud_none(*pud)) {
            printk("not mapped in pud\n");
            return -1;
        }
        pmd = pmd_offset(pud, vaddr);
        printk("pmd_val = 0x%lx\n", pmd_val(*pmd));
        printk("pmd_index = %lu\n", pmd_index(vaddr));
        if (pmd_none(*pmd)) {
            printk("not mapped in pmd\n");
            return -1;
        }
        pte = pte_offset_kernel(pmd, vaddr);
        printk("pte_val = 0x%lx\n", pte_val(*pte));
        printk("pte_index = %lu\n", pte_index(vaddr));
        if (pte_none(*pte)) {
            printk("not mapped in pte\n");
            return -1;
        }
        //页框物理地址机制 | 偏移量
        page_addr = pte_val(*pte) & PAGE_MASK;
        page_offset = vaddr & ~PAGE_MASK;
        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 __init v2p_init(void)
    {
        unsigned long vaddr = 0;
        printk("vaddr to paddr module is running..\n");
        get_pgtable_macro();
        printk("\n");
        vaddr = (unsigned long)vmalloc(1000 * sizeof(char));
        if (vaddr == 0) {
            printk("vmalloc failed..\n");
            return 0;
        }
        printk("vmalloc_vaddr=0x%lx\n", vaddr);
        vaddr2paddr(vaddr);
        printk("\n\n");
        vaddr = __get_free_page(GFP_KERNEL);
        if (vaddr == 0) {
            printk("__get_free_page failed..\n");
            return 0;
        }
        printk("get_page_vaddr=0x%lx\n", vaddr);
        vaddr2paddr(vaddr);
        return 0;
    }
    static void __exit v2p_exit(void)
    {
        printk("vaddr to paddr module is leaving..\n");
            vfree((void *)vaddr);
            free_page(vaddr);
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88

    整个程序的结构如下:

    get_pgtable_macro()打印当前系统分页机制中的一些宏。

    通过vmalloc()在内核空间中分配内存,调用vaddr2paddr()将虚拟地址转化成物理地址。

    通过__get_free_pages()在内核空间中分配页框,调用vaddr2paddr()将虚拟地址转化成物理地址。

    分别通过vfree()和free_page()释放申请的内存空间。

    vaddr2paddr()的执行过程如下:

    1. 通过pgd_offset计算页全局目录项的线性地址pgd,传入的参数为内存描述符mm和线性地址vaddr。接着打印pgd所指的页全局目录项。

    2. 通过pud_offset计算页上级目录项的线性地址pud,传入的参数为页全局目录项的线性地址pgd和线性地址vaddr。接着打印pud所指的页上级目录项。

    3. 通过pmd_offset计算页中间目录项的线性地址pmd,传入的参数为页上级目录项的线性地址pud和线性地址vaddr。接着打印pmd所指的页中间目录项。

    4. 通过pte_offset_kernel计算页表项的线性地址pte,传入的参数为页中间目录项的线性地址pmd和线性地址vaddr。接着打印pte所指的页表项。

    5. pte_val(*pte)先取出页表项,与PAGE_MASK相与的结果是得到要访问页的物理地址;vaddr&~PAGE_MASK用来得到线性地址offset字段;两者或运算得到最终的物理地址。

    2. 内核虚拟地址转换成物理地址

    __pa():将物理内核虚拟地址转换成物理虚拟地址

    #include <asm/page.h>
    #define __pa(x)		__phys_addr((unsigned long)(x))
    
    • 1
    • 2

    3. 物理地址转换成内核虚拟地址

    __va()将物理地址转换成内核虚拟地址:

    #include <asm/page.h>
    #define __va(x)			((void *)((unsigned long)(x)+PAGE_OFFSET))
    
    • 1
    • 2

    4 内核虚拟地址和对应页

    struct page *virt_to_page(void *kaddr); 
    //将内核逻辑地址转换为响应的page结构指针。
    struct page *pfn_to_page(int pfn);
    //针对给定的页帧号,返回page结构指针。
    void *page_address(struct page *page); 
    //如果地址存在的话,则返回页的内核虚拟地址
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    5 根据进程号获取进程描述符

    struct task_struct * task = pid_task(find_vpid(target_pid), PIDTYPE_PID);
    
    • 1

    find_vpid:此函数根据提供的局部进程号获取对应的进程描述符

    struct pid *find_vpid(int nr)
    
    • 1

    pid_task:此函数获取任务的任务描述符信息,此任务在进程pid的使用链表中,并且搜索的链表的起始元素的下标为参数type的值。

    struct task_struct *pid_task(struct pid *pid, enum pid_type)
    
    enum pid_type
    {
        PIDTYPE_PID,     //进程的进程号
        PIDTYPE_PGID,    //进程组领头进程的进程号
        PIDTYPE_SID,     //会话领头进程的进程号
        PIDTYPE_MAX
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
  • 相关阅读:
    SpringBoot运维实用篇
    OSPF高级配置——虚接口,NSSA
    [蓝桥杯复盘] 第 3 场双周赛20231111
    makkefile文件自动化编译以及基础文件命令(补)
    数字化管理门店| 甜品店管理系统
    请求转发(J2EE)
    神经网络与深度学习(二):前馈神经网络
    HJ23 删除字符串中出现次数最少的字符
    SpringMVC之拦截器
    Redis代码实践总结(三)——redis持久化
  • 原文地址:https://blog.csdn.net/qq_41683305/article/details/124931018