Chinaunix首页 | 论坛 | 博客
  • 博客访问: 38920
  • 博文数量: 23
  • 博客积分: 1065
  • 博客等级: 少尉
  • 技术积分: 185
  • 用 户 组: 普通用户
  • 注册时间: 2006-11-14 15:32
文章分类

全部博文(23)

文章存档

2010年(20)

2008年(3)

我的朋友
最近访客

分类: LINUX

2010-03-02 00:02:01

先问个问题,在pagetable_init中只初始化了3G-3G+物理内存这一段虚拟空间,vmalloc分配的从3G+物理内存+8M—4G的虚拟 空间的页目录是什么时候初始化的,进而链表vmlist是什么时候初始化的?

在vmalloc和ioremap的时候因为分配的是内核虚拟空间,所以要设置相应的页目录和页表项(ioremap的过程与vmalloc的基本上相 同)
/*
 * Allocate any pages
 */
 
static inline void * vmalloc (unsigned long size)
{
 return __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL);
}



void * __vmalloc (unsigned long size, int gfp_mask, pgprot_t prot)
{
 void * addr;
 struct vm_struct *area;
>>>>首先将预分配的长度页面对齐
 size = PAGE_ALIGN(size);
>>>>如果分配长度为零,或者明显过大,则拒绝分配
 if (!size || (size >> PAGE_SHIFT) > num_physpages) {
  BUG();
  return NULL;
 }
 area = get_vm_area(size, VM_ALLOC);
 if (!area)
  return NULL;
 addr = area->addr;

 if (vmalloc_area_pages(VMALLOC_VMADDR(addr), size, gfp_mask, prot)) {
  vfree(addr);
  return NULL;
 }
 return addr;
}


struct vm_struct * get_vm_area(unsigned long size, unsigned long flags)
{
 unsigned long addr;
 struct vm_struct **p, *tmp, *area;
>>>>使用kmalloc分配一个vm_struct结构,它用来描述vmalloc分配的内存块
 area = (struct vm_struct *) kmalloc(sizeof(*area), GFP_KERNEL);
 if (!area)
  return NULL;
>>>>size加一个页面的长度,使中间形成4K的隔离带
 size += PAGE_SIZE;
>>>>get_vm_area返回从VMALLOC_START到VMALLOC_END之间一段自由内存区间
>>>>VMALLOC_START和VMALLOC_END定义在pgtable.h中
 addr = VMALLOC_START;
 write_lock(&vmlist_lock);
 for (p = &vmlist; (tmp = *p) ; p = &tmp->next) {
  if ((size + addr) < addr) {
   write_unlock(&vmlist_lock);
   kfree(area);
   return NULL;
  }
>>>>如果两个已分配区域中间的空间满足要求,则跳出循环。此时addr是最小的可用地址
  if (size + addr < (unsigned long) tmp->addr)
   break;
>>>>从下一个已经分配区域之后的地址开始
  addr = tmp->size + (unsigned long) tmp->addr;
  if (addr > VMALLOC_END-size) {
   write_unlock(&vmlist_lock);
   kfree(area);
   return NULL;
  }
 }
 area->flags = flags;
 area->addr = (void *)addr;
 area->size = size;
 area->next = *p;
 *p = area;
 write_unlock(&vmlist_lock);
 return area;
}


inline int vmalloc_area_pages (unsigned long address, unsigned long size,
                               int gfp_mask, pgprot_t prot)
{
 pgd_t * dir;
 unsigned long end = address + size;
 int ret;

>>>>#define pgd_offset_k(address) pgd_offset(&init_mm, address)
>>>>#define pgd_offset(mm, address) ((mm)->pgd+pgd_index(address))

>>>>得到地址address在内核页目录中的项
 dir = pgd_offset_k(address);

 flush_cache_all();
 spin_lock(&init_mm.page_table_lock);
 do {
  pmd_t *pmd;

>>>#define pmd_alloc_kernel(pgd, address) pmd_alloc_mm(NULL, pgd, address)
>>>#define pmd_alloc_mm(mm, pgd, address) pmd_alloc(pgd, address)
>>>extern inline pmd_t * pmd_alloc(pgd_t *pgd, unsigned long address)
>>>{
>>> if (!pgd)
>>>  BUG();
>>> return (pmd_t *) pgd;
>>>}
>>> 实际上返回*dir
>>> 问题:这个*dir是在什么时候初始化的? 
  pmd = pmd_alloc_kernel(dir, address);
  ret = -ENOMEM;
  if (!pmd)
   break;

  ret = -ENOMEM;
>>>>分配页表
  if (alloc_area_pmd(pmd, address, end - address, gfp_mask, prot))
   break;
>>>>一个目录项映射(1K项页表)*(每项页表映射4K)=PGDIR_SIZE=1UL<<22=4M的物理 页面
  address = (address + PGDIR_SIZE) & PGDIR_MASK;
>>>>下一个目录项,虚拟地址连续
  dir++;
  ret = 0;
 } while (address && (address < end));
 spin_unlock(&init_mm.page_table_lock);
 flush_tlb_all();
 return ret;
}

static inline int alloc_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size, int gfp_mask, pgprot_t prot)
{
 unsigned long end;

 address &= ~PGDIR_MASK;
 end = address + size;
 if (end > PGDIR_SIZE)
  end = PGDIR_SIZE;
 do {
>>> 分配页表
  pte_t * pte = pte_alloc_kernel(pmd, address);
  if (!pte)
   return -ENOMEM;
>>> 分配页面
  if (alloc_area_pte(pte, address, end - address, gfp_mask, prot))
   return -ENOMEM;
  address = (address + PMD_SIZE) & PMD_MASK;
  pmd++;
 } while (address < end);
 return 0;
}


static inline int alloc_area_pte (pte_t * pte, unsigned long address,
   unsigned long size, int gfp_mask, pgprot_t prot)
{
 unsigned long end;

 address &= ~PMD_MASK;
 end = address + size;
 if (end > PMD_SIZE)
  end = PMD_SIZE;
 do {
  struct page * page;
  spin_unlock(&init_mm.page_table_lock);
>>>>根据gftp_mask的要求,从适当的区分配1个页面,返回第一个页的描述符
  page = alloc_page(gfp_mask);
  spin_lock(&init_mm.page_table_lock);
  if (!pte_none(*pte))
   printk(KERN_ERR "alloc_area_pte: page already exists\n");
  if (!page)
   return -ENOMEM;
>>>>填充页表项的物理地址和保护域
  set_pte(pte, mk_pte(page, prot));
>>>>设置下一个页面
  address += PAGE_SIZE;
  pte++;
 } while (address < end);
 return 0;
}

#define set_pte(pteptr, pteval) (*(pteptr) = pteval)
>>>>page-mem_map得到页面的物理页帧号
#define mk_pte(page, pgprot) __mk_pte((page) - mem_map, (pgprot))
>>>>物理页帧号右移20位,填充页表项的物理地址
#define __mk_pte(page_nr,pgprot) __pte(((page_nr) << PAGE_SHIFT) | pgprot_val(pgprot))
#define __pte(x) ((pte_t) { (x) } )
阅读(519) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~