linux内存管理之非连续物理地址分配(vmalloc)(1)(3)
//运行到这里的时候,已经断开了一个页目录所表示的线性地址,而每个页目录表示的线性地址//大小为PGDIR_SIZE
address = (address + PGDIR_SIZE) & PGDIR_MASK;
dir++;
} while (address && (address < end));
//当到达末尾时结束循环
flush_tlb_kernel_range((unsigned long) area->addr, end);
}
//断开线性地址区间所在的pmd的映射
static void unmap_area_pmd(pgd_t *dir, unsigned long address,
unsigned long size)
{
unsigned long end;
pmd_t *pmd;
if (pgd_none(*dir))
return;
if (pgd_bad(*dir)) {
pgd_ERROR(*dir);
pgd_clear(dir);
return;
}
pmd = pmd_offset(dir, address);
address &= ~PGDIR_MASK;
end = address + size;
if (end > PGDIR_SIZE)
end = PGDIR_SIZE;
do {
//断开线性地址所在的pte的映射关系
unmap_area_pte(pmd, address, end - address);
address = (address + PMD_SIZE) & PMD_MASK;
pmd++;
} while (address < end);
}
static void unmap_area_pte(pmd_t *pmd, unsigned long address,
unsigned long size)
{
unsigned long end;
pte_t *pte;
if (pmd_none(*pmd))
return;
if (pmd_bad(*pmd)) {
pmd_ERROR(*pmd);
pmd_clear(pmd);
return;
}
pte = pte_offset_kernel(pmd, address);
address &= ~PMD_MASK;
end = address + size;
if (end > PMD_SIZE)
end = PMD_SIZE;
do {
pte_t page;
//清除pte的对应映射关系
page = ptep_get_and_clear(pte);
address += PAGE_SIZE;
pte++;
if (pte_none(page))
continue;
if (pte_present(page))
continue;
printk(KERN_CRIT "Whee.. Swapped out page in kernel page table\n");
} while (address < end);
}
经过这几个过程之后,实际上,它只是找到线性地址所对应的pte,然后断开pte的映射.值得注意的是:为了效率起见,这里只是断开了pte的映射,即只是将pte置为none,表示pte末映射内存.并末断开pmd和pgd的映射
三:vmalloc的实现:
void *vmalloc(unsigned long size)
{
return __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL);
}
实际上调用__vmalloc:
void *__vmalloc(unsigned long size, int gfp_mask, pgprot_t prot)
{
struct vm_struct *area;
struct page **pages;
unsigned int nr_pages, array_size, i;
//使请求的大小与页框对齐
size = PAGE_ALIGN(size);
//有效性检查
if (!size || (size >> PAGE_SHIFT) > num_physpages)
return NULL;
评论暂时关闭