1,输出每个进程的虚拟内存区域 2,在当前进程中给定某个虚拟地址,找出它在那个虚拟区域 3,根据当前进程的页表,给定某个虚拟地址,输出映射到的物理地址,若还没有映射,输出缺页。 4,在某个虚拟地址上写入自己的数据。 5,输出当前进程的四级页表的内容。1,输出每个进程的虚拟地址区域 在0-3G的虚拟用户空间,通过struct vm_area_struct 结构把它化为了几个区域,每个区域用一个vm_area_struct结构表示<pre name="code" class="cpp">#include <linux/init.h> #include <linux/module.h> #include <asm/uaccess.h> #include <linux/kernel.h> #include <linux/mm.h> #include <linux/mm_types.h> #include <linux/sched.h> static void listvma(void) { struct vm_area_struct *vma; int count; vma=current->mm->mmap; printk("task pid=%d,comm is %s\n",current->pid,current->comm); count=0; for(;vma;vma=vma->vm_next) { printk("vma[%d] start:%lx----end:%lx\n",count,vma->vm_start,vma->vm_end); count++; } } static int __init listvma_init(void) { printk("listvma is working...\n"); listvma(); return 0; } static void __exit listvma_exit(void) { printk("listvma is leaving....\n"); } module_init(listvma_init); module_exit(listvma_exit);
结果如图:
2,给定某个虚拟地址,找出其所在域 代码如下#include <linux/init.h> #include <linux/module.h> #include <asm/uaccess.h> #include <linux/mm.h> #include <linux/mm_types.h> #include <linux/sched.h> static ulong addr=0; static void findvma(void) { struct mm_struct *mm=current->mm; struct vm_area_struct *vma; for(vma=mm->mmap;vma;vma=vma->vm_next) { printk("%lx----%lx\n",vma->vm_start,vma->vm_end); } vma=find_vma(mm,addr); if(vma&&addr<=vma->vm_start) { printk("addr %lx found in the vma:%lx-%lx\n", addr,vma->vm_start,vma->vm_end); } else printk("addr %lx not found int current task\n", addr); } static int __init findvma_init(void) { printk("find vma is working....\n"); findvma(); return 0; } static void __exit findvma_exit(void) { printk("find vma is leaving....\n"); } module_init(findvma_init); module_exit(findvma_exit); module_param(addr,ulong,0644);
3,给定某个虚拟地址,找出其相应的物理地址。 在linux中,我们前面讨论过的都是二级页表的机制,事实上,现在适用的都是四级机制了,即把虚拟地址分为 页全局目录pgd 页上级目录pud 页中级目录pmd 页表 pt (写为pte) 偏移 offset 其中,对于x86-32(未开启pae)来说,pud和pmd的位数都为0,这就是前面的二级分页机制,兼容了。 pgd_t pud_t pmd_t pte_t 本质上都是unsigned long型 例子:如果一个pgd_t型的地址pgd,那么通过pgd_val(*pgd)可以获得该地址的内容。其他类似。 下面看个图:41代码如下:
#include <linux/init.h>
#include <linux/module.h>
#include <asm/uaccess.h>
#include <linux/mm.h>
#include <linux/mm_types.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
static ulong vaddr=0;
module_param(vaddr,ulong,0644);
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;
int i;
struct mm_struct *mm=current->mm;
pgd_t *pg=mm->pgd;
// for(i=0;i<10;i++,pg++)
// printk("pd[%d]\t 0x%lx\n",i,*pg);
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_t *pu=(pud_t*)(pgd_val(*pgd));
//for(i=0;i<100;i++,pu++)
// printk("pu[%d]\t 0x%lx\n",i,*pu);
pud=pud_offset(pgd,vaddr);
printk("pud_val=%lx\n",pud_val(*pud));
// printk("pud_index=%lu\n",pud_index(vaddr));
if(pud_none(*pud))
{
printk("not mapped in pud\n");
return -1;
}
pmd=pmd_offset(pud,vaddr);
printk("pmd_val=%lx\n",pmd_val(*pmd));
printk("pmd_indext=%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=%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;// PAGE_MASK=0xfffff000;
paddr=page_addr | page_offset;
//这里应该是page_addr+page_offset,但是这里,page_addr是
//低12位为0的32地址,page_offset是高20位的32地址,
//这样它们与后的结果与加的结果是一样的。
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)
{
printk("v2p is working ...\n");
vaddr2paddr(vaddr);
return 0;
}
static void __exit v2p_exit(void)
{
printk("v2p is leaving...\n");
}
module_init(v2p_init);
module_exit(v2p_exit);
结果:
每次我实验的虚拟地址好像都没有映射到物理地址上面,很遗憾,看不到最后结果
。
4,在某个虚拟地址上写入数据 其实上个程序已经把虚拟地址对应的物理地址找到了,那么主要在上面写东西就可以了,这里不贴代码了。
5,输出当前进程四级页表的内容。 在代码3中,寻找物理地址时候,就已经触及到各级页表了,这里只需要把它们输出而已,代码如下。 在32位机器上,pmd和pud都是0位,不必输出了 pgd 10位 pte 10位 偏移 12位
代码如下:
#include <linux/init.h>
#include <linux/module.h>
#include <asm/uaccess.h>
#include <linux/mm.h>
#include <linux/mm_types.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
static void display(void)
{
pgd_t *pgd;
pte_t *pte;
int i,j;
struct mm_struct *mm=current->mm;
pgd_t *pg=mm->pgd;
pte_t *pt;
unsigned long p1,p2;
for(i=0;i<1024;i++,pg++)
{
printk("pd[%d]\t 0x%lx\n",i,*pg);
p1=pgd_val(*pg);
if(p1==0)
{
printk("this pagetable is empty,contine\n");
continue;
}
printk("pgtable %d--------------\n",i);
pt=pte_offset_kernel(pg,0);
for(j=0;j<1024;j++,pt++)
{
if(j%10==0)
printk("\n");
printk("pt[%d] 0x%lx ",j,*pt);
}
}
}
static int __init v2p_init(void)
{
printk("v2p is working ...\n");
display();
return 0;
}
static void __exit v2p_exit(void)
{
printk("v2p is leaving...\n");
}
module_init(v2p_init);
module_exit(v2p_exit);
结果:
<img src="https://img-blog.csdn.net/20140731180128362?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMjU2NjE4MQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />
、<img src="https://img-blog.csdn.net/20140731175913515?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMjU2NjE4MQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />