轉載:虛擬地址轉換成爲物理地址

轉載自: http://blog.chinaunix.net/uid-26874138-id-3219811.html

虛擬地址轉換成爲物理地址 

實驗知識點

<1>對於用戶的虛擬空間是用mm_struct進行描述和組織的。 對於整個用戶的虛擬區又是通過vm_area_struct 結構體進行管理的。對於物理頁面是通過 pgd_t 結構體進行組織和管理的。

對於虛存區的組織是通過兩種方式進行組織的:鏈式和紅黑樹。

         Linux2.4中的mm_struct 的源碼爲:

    struct mm_struct
    {
    struct vm_area_struct *mmap;/* 指向線性區對象的鏈表頭*/
    rb_root_t mm_rb; /* 指向線性區對象的紅黑樹*/
    struct vm_area_struct * mmap_cache;/* 指向最後一個引用的線性區對
    象*/
    pgd_t *pgd;
    atomic_t mm_users;/*多少個用戶使用該用戶空間*/
    int map_count;/*虛存區的個數*/
    struct rw_semaphore mmap_sem;/* 線性區的自旋鎖和頁表的自旋鎖*/
    spinlock_t page_table_lock;/*存放鏈表相鄰元素的地址,第一個元素
    是init_mm的mm_list字段*/
    struct list_head mmlist;/*用雙向循環鏈表對用戶空間進行組織*/
    unsigned long start_code,end_code,start_data,end_data;
    /*代碼段的起始地址,代碼段的最後地址,數據段的起始地址和數據段
    的最後的地址*/
    unsigned long start_brk,brk,start_stack;
    /*堆的起始地址,堆的當前最後地址,用戶態堆棧的起始地址*/
    unsigned long arg_start,arg_end,env_start,env_end;
    /*命令行參數的起始地址,命令行參數的最後地址,環境變量的起始地
    址,環境變量的最後地址*/
    unsigned long rss, total_vm,locked_vm;
    /*進程駐留在物理內存中的頁面數目、進程所需要的總頁數、被鎖定
    在物理內存中的頁面數*/
    unsigned long def_flags;/*線性區默認的訪問標誌*/
    unsigned long cpu_vm_mask;
    unsigned long swap_addsess;
    unisigned dumpable :1;
    mm_context_t context;
    };



       Linux 2.4中的 struct vm_area_struct  的源碼爲

    struct vm_area_struct
    {
    struct mm_struct * vm_mm; /*虛擬區所屬的用戶空間*/
    unsigned long vm_start; /*虛擬區開始的地址*/
    unsigned long vm_end; /*虛擬區結束的地址*/
    struct vm_area_struct *vm_next;/*鏈接虛存區*/
    pgprot_t vm_page_prot; /*虛存區的保護權限*/
    unsigned long vm_flags; /*虛存區的標誌*/
    short vm_avl_height;/*AVL的高度*/
    struct vm_area_struct * vm_avl_left; /*左虛存區節點*/
    struct vm_area_struct * vm_avl_right;/*右虛存區節點*/
    struct vm_area_struct *vm_next_share;
    struct vm_area_struct **vm_pprev_share;
    struct vm_operations_struct * vm_ops;/*對虛存區操作的函數*/
    unsigned long vm_pgoff; /* 映射文件中的偏移量*/
    struct file * vm_file;/*指向映射文件的文件對象*/
    unsigned long vm_raend;/
    void * vm_private_data; /*指向內存區的私有數據*/
    };
    Linux 2.4中的 pgd_t的源碼爲
    typedef struct page
    {
    struct list_head list;/*頁面通過雙向鏈表進程組織*/
    struct address_space *mapping;
    unsigned long index;/* 映射中的偏移量*/
    struct page *next_hash;/*哈希中的下一個*/
    atomic_t count;/*該頁面被使用的個數*/
    unsigned long flags;
    struct list_head lru;
    unsigned long age;
    wait_queue_head_t wait;/*等待隊列*/
    struct page **pprev_hash;
    struct buffer_head * buffers;
    void *virtual; /* non-NULL if kmapped */
    struct zone_struct *zone;
    } mem_map_t;




<2>以上兩個數據結構之間的關係。

   

    <3> 虛存區的設計:

    對於一個虛存區會被分爲5部分:頁全局目錄PGD、頁上級目錄PUD、頁中間級目錄PMD、頁表PT、偏移量offset。其中表項叫做頁表項PTE

三、程序的分析

  • 首先重進程描述符中(mm_struct)讀取pgd的字段的內容,他就是頁全局目錄的起始地址;

  • 然後頁全局目錄起始地址加上頁全局目錄索引獲得頁上級目錄的起始地址;頁上級目錄的起始地址加上頁上級目錄的索引獲得頁中間目錄的起始地址;頁中間目錄的起始地址加上頁中間目錄的索引獲得頁表的起始地址;也表的起始地址加上索引,可以得到完整的頁表項內容;

  • 從頁表項中取出物理地址的基址,加上偏移量可以得到最終的物理地址;

四、代碼

address.c

    #include<linux/kernel.h>
    #include<linux/sched.h>
    #include<linux/module.h>
    #include<linux/init.h>
    #include<linux/mm.h>
    #include<linux/mman.h>
    #include<linux/highmem.h>
    MODULE_LICENSE("GPL");
    static int pid;
    static unsigned long va;
    module_param(pid, int ,0644);
    module_param(va, long ,0644);
    static struct page * my_follow_page( struct vm_area_struct *vma,unsigned long addr)
    {
        pgd_t *pgd=NULL;
        pud_t *pud=NULL;
        pmd_t *pmd=NULL;
        pte_t *pte=NULL;
        spinlock_t *ptl=NULL;
        unsigned long full_addr;
        struct page *page=NULL;
        struct mm_struct *,mm = vma->mm;
        printk("mm = %lx\n",*mm);
        pgd = pgd_offset(mm,addr);
        printk("pgd = %lx\n",*pgd);
        if(pgd==NULL||unlikely(pgd_bad(*pgd)))
            goto out;
        pud = pud_offset(pgd,addr);
        printk("pud = %lx\n",*pud);
        if(pud_none(*pud)||unlikely(pud_bad(*pud)))
            goto out;
     
        pmd = pmd_offset(pud,addr);
        printk("pmd = %lx\n",*pmd);
        if(pmd_none(*pmd)||unlikely(pmd_bad(*pmd)))
            goto out;
        pte = pte_offset_map_lock(mm,pmd,addr,&ptl);
        if(!pte)
            goto out;
        if(!pte_present(*pte))
        {    
                printk("1 unlock\n");
                goto unlock;
        }
        page = pfn_to_page(pte_pfn(*pte));
        if(!page) {
            printk("0 unlock\n");
            goto unlock;
        }
        full_addr = (*pte).pte_low&PAGE_MASK;
        full_addr+=addr&(~PAGE_MASK);
        printk("full_addr = %lx....\n",full_addr);
        printk("pte = %lx ...........\n",pte_pfn(*pte));
        printk("page = %p......\n",page);
        get_page(page);
        unlock:
         pte_unmap_unlock(pte,ptl);
        out:
         return page;
    }
    static int __init physical_init(void)
    {
        
        struct vm_area_struct *p;
        /*添加一個函數求得虛存區,存入p中*/
        my_follow_page(p,va);
        return 0;
    }
    static void __exit physical_exit(void)
    {
        printk("bye !");
    }
    module_init(physical_init);
    module_exit(physical_exit);


五、運行命令

      sudo insmod  address.ko pid=1342  va=0x34223230

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章