转载自: 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