感受虚拟内存

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)来说,pudpmd的位数都为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位机器上,pmdpud都是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="" /> 







                
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值