先问个问题,在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) |