Chinaunix首页 | 论坛 | 博客
  • 博客访问: 271584
  • 博文数量: 95
  • 博客积分: 2047
  • 博客等级: 大尉
  • 技术积分: 1022
  • 用 户 组: 普通用户
  • 注册时间: 2011-03-14 16:18
文章分类

全部博文(95)

文章存档

2013年(1)

2011年(94)

我的朋友

分类: 嵌入式

2011-08-30 23:09:38

static int v2p(unsigned long va) 
{     
unsigned long pa = 0;     
struct task_struct *pcb_tmp;     
pgd_t *pgd_tmp = NULL;     
pud_t *pud_tmp = NULL;     
pmd_t *pmd_tmp = NULL;     
pte_t *pte_tmp = NULL;      
printk(KERN_INFO"PAGE_OFFSET = 0x%lx\n",PAGE_OFFSET);     
printk(KERN_INFO"PGDIR_SHIFT = %d\n",PGDIR_SHIFT);     
printk(KERN_INFO"PUD_SHIFT = %d\n",PUD_SHIFT);     
printk(KERN_INFO"PMD_SHIFT = %d\n",PMD_SHIFT);     
printk(KERN_INFO"PAGE_SHIFT = %d\n",PAGE_SHIFT);      
printk(KERN_INFO"PTRS_PER_PGD = %d\n",PTRS_PER_PGD);     
printk(KERN_INFO"PTRS_PER_PUD = %d\n",PTRS_PER_PUD);     
printk(KERN_INFO"PTRS_PER_PMD = %d\n",PTRS_PER_PMD);     
printk(KERN_INFO"PTRS_PER_PTE = %d\n",PTRS_PER_PTE);      
printk(KERN_INFO"PAGE_MASK = 0x%lx\n",PAGE_MASK);      

pcb_tmp = current;     
printk(KERN_INFO"pgd = 0x%p\n",pcb_tmp->mm->pgd);     

if(!find_vma(pcb_tmp->mm,va))
{                     
printk(KERN_INFO"virt_addr 0x%lx not available.\n",va);                     
return 0;     
}     

pgd_tmp = pgd_offset(pcb_tmp->mm,va);     
printk(KERN_INFO"pgd_tmp = 0x%p\n",pgd_tmp);     
printk(KERN_INFO"pgd_val(*pgd_tmp) = 0x%lx\n",pgd_val(*pgd_tmp));     

if(pgd_none(*pgd_tmp))
{                     
printk(KERN_INFO"Not mapped in pgd.\n");                                     
return 0;     
}     

pud_tmp = pud_offset(pgd_tmp,va);     
printk(KERN_INFO"pud_tmp = 0x%p\n",pud_tmp);     
printk(KERN_INFO"pud_val(*pud_tmp) = 0x%lx\n",pud_val(*pud_tmp));     

if(pud_none(*pud_tmp))
{                     
printk(KERN_INFO"Not mapped in pud.\n");                     
return 0;     
}     

pmd_tmp = pmd_offset(pud_tmp,va);     
printk(KERN_INFO"pmd_tmp = 0x%p\n",pmd_tmp);     
printk(KERN_INFO"pmd_val(*pmd_tmp) = 0x%lx\n",pmd_val(*pmd_tmp));     

if(pmd_none(*pmd_tmp)){                     
printk(KERN_INFO"Not mapped in pmd.\n");                     
return 0;     
}      

pte_tmp = pte_offset_kernel(pmd_tmp,va);      
printk(KERN_INFO"pte_tmp = 0x%p\n",pte_tmp);     
printk(KERN_INFO"pte_val(*pte_tmp) = 0x%lx\n",pte_val(*pte_tmp));     

if(pte_none(*pte_tmp))
{                     
printk(KERN_INFO"Not mapped in pte.\n");                     
return 0;     
}     

if(!pte_present(*pte_tmp))
{                     
printk(KERN_INFO"pte not in RAM.\n");                     
return 0;     
}     

pa = (pte_val(*pte_tmp) & PAGE_MASK) |(va & ~PAGE_MASK);     
printk(KERN_INFO"virt_addr 0x%lx in RAM is 0x%lx .\n",va,pa);     
printk(KERN_INFO"contect in 0x%lx is 0x%lx\n",pa, *(unsigned long *)((char *)pa + PAGE_OFFSET));  
                                                                                                        
return 0; 



对页表项操作的宏
宏名称 说明
pgd_index(addr) 找到线性地址addr 对应的的目录项在页全局目录中的索引(相对位
置)。
pgd_offset(mm, addr) 接收内存描述符地址mm(参见第九章)和线性地址addr作为参数。这个宏
产生地址addr在页全局目录中相应表项的线性地址;通过内存描述符mm内的一个指针可以找到这个页全
局目录。
pgd_offset_k(addr) 产生主内核页全局目录中的某个项的线性地址,该项对应于地址addr
(参见稍后“内核页表”一节)。
pgd_page(pgd) 通过页全局目录项pgd产生页上级目录所在页框的页描述符地址。在两
级或三级分页系统中,该宏等价于pud_page(),后者应用于页上级目录项。
pud_offset(pgd, addr) 参数为指向页全局目录项的指针pgd和线性地址addr。这个宏产生页上级目
录中目录项addr对应的线性地址。在两级或三级分页系统中,该宏产生pgd,即一个页全局目录项的地址。
pud_page(pud) 通过页上级目录项pud产生相应的页中间目录的线性地址。在两级分页
系统中,该宏等价于pmd_page(),后者应用于页中间目录项。
pmd_index(addr) 产生线性地址addr在页中间目录中所对应目录项的索引(相对位置)。
pmd_offset(pud, addr) 接收指向页上级目录项的指针pud和线性地址addr作为参数。这个宏产生目
录项addr在页中间目录中的偏移地址。在两级或三级分页系统中,它产生pud,即页全局目录项的地址。
pmd_page(pmd) 通过页中间目录项pmd产生相应页表的页描述符地址。在两级或三级分
页系统中,pmd实际上是页全局目录中的一项。
mk_pte(p,prot) 接收页描述符地址p和一组访问权限prot作为参数,并创建相应的页表
项。
pte_index(addr) 产生线性地址addr对应的表项在页表中的索引(相对位置)。
pte_offset_kernel(dir, addr) 线性地址addr在页中间目录dir中有一个对应的项,该宏就产生这个对应
项,即页表的线性地址。另外,该宏只在主内核页表上使用(参见稍后“内
核页表”一节)。
pte_offset_map(dir, addr) 接收指向一个页中间目录项的指针dir和线性地址addr作为参数,它产生与
线性地址addr相对应的页表项的线性地址。如果页表被保存在高端存储器中,那么内核建立一个临时内核映
射(参见第八章“高端内存页框的内核映射”一节),并用pte_unmap对它进行释放。pte_offset_map_nested
宏和pte_unmap_nested宏是相同的,但它们使用不同的临时内核映射。
pte_page( x ) 返回页表项x所引用页的描述符地址。
pte_to_pgoff( pte ) 从一个页表项的pte字段内容中提取出文件偏移量,这个偏移量对应着
一个非线性文件内存映射所在的页(参见第十六章“非线性存储器映射”一
节)。
pgoff_to_pte(offset ) 为非线性文件内存映射所在的页创建对应页表项的内容。
这里罗列最后一组函数来简化页表项的创建和撤消。
当使用两级页表时,创建或删除一个页中间目录项是不重要的。如本节前部分所述,页中间目录仅含有一个
指向下属页表的目录项。所以,页中间目录项只是页全局目录中的一项而已。然而当处理页表时,创建一个
页表项可能很复杂,因为包含页表项的那个页表可能就不存在。在这样的情况下,有必要分配一个新页框,
把它填写为0,并把这个表项加入。
如果PAE被激活,内核使用三级页表。当内核创建一个新的页全局目录时,同时也分配四个相应的页中间目
录;只有当父页全局目录被释放时,这四个页中间目录才得以释放。
当使用两级或三级分页时,页上级目录项总是被映射为页全局目录中的一个单独项。
与以往一样,表28
中列出的函数描述是针对80x86构架的。






虚拟地址转换为物理地址

应用程序只能提供一个虚拟地址,也可以通过如下方法获取物理地址。

Linux采用页表的概念来管理虚拟空间,内核在处理虚拟地址时都必须将其转换为物理地址,然后处理器才能够访问。虚拟地址可以通过Linux的页表操作宏逐层查找到物理地址,简单来说需要将虚拟地址分段,每段地址都作为索引指向页表,最后一级页表指向物理地址。
Linux在2.6.11以后版本为了兼容各种处理器,采用四级页表结构:
PGD:Page Global Directory,页全局目录,是顶级页表。
PUD:Page Upper Directory,页上级目录,是第二级页表
PMD:Page Middle Derectory,页中间目录,是第三级页表。
PTE:Page Table Entry,页面表,最后一级页表,指向物理页面。
可以通过数据结构mm_struct访问PGD找到物理页面,如图4-8,根据页表寻找物理地址的流程见4-9。
 
图  Linux采用的4级页面
 
简化的转换代码如下:
static int vir2phy(unsigned long va) 
{     
struct task_struct *pcb_tmp;     
pcb_tmp = current;     
pgd_tmp = pgd_offset(pcb_tmp->mm,va);     
pud_tmp = pud_offset(pgd_tmp,va);     
pmd_tmp = pmd_offset(pud_tmp,va);     
pte_tmp = pte_offset_kernel(pmd_tmp,va);      
pa = (pte_val(*pte_tmp) & PAGE_MASK) |(va & ~PAGE_MASK);     
return pa; 
}

pgd_offset(mm, addr) 接收内存描述符地址mm和线性地址addr作为参数。这个宏产生地址addr在页全局目录中相应表项的线性地址;
通过内存描述符mm内的一个指针可以找到这个页全局目录。

pud_offset(pgd, addr) 参数为指向页全局目录项的指针pgd和线性地址addr。这个宏产生页上级目录中目录项addr对应的线性地址。在两级或三级分页系统中,该宏产生pgd,即一个页全局目录项的地址。

pmd_offset(pud, addr) 接收指向页上级目录项的指针pud和线性地址addr作为参数。这个宏产生目录项addr在页中间目录中的偏移地址。在两级或三级分页系统中,它产生pud,即页全局目录项的地址。

pte_offset_kernel(dir, addr) 线性地址addr在页中间目录dir中有一个对应的项,该宏就产生这个对应项,即页表的线性地址。另外,该宏只在主内核页表上使用。
阅读(1353) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~