linux内存管理之非连续物理地址分配(vmalloc)(1)(2)
//分配一个vm_struct结构空间
area = kmalloc(sizeof(*area), GFP_KERNEL);
if (unlikely(!area))
return NULL;
//PAGE_SIZE:在i32中为4KB,即上面所说的间隔空洞
size += PAGE_SIZE;
if (unlikely(!size)) {
kfree (area);
return NULL;
}
write_lock(&vmlist_lock);
//遍历vmlist:找到合适大小的末使用空间
for (p = &vmlist; (tmp = *p) != NULL ;p = &tmp->next) {
//若起始地址落在某一个vm区间,则调整起始地址为vm区间的末尾
if ((unsigned long)tmp->addr < addr) {
if((unsigned long)tmp->addr + tmp->size >= addr)
addr = ALIGN(tmp->size +
(unsigned long)tmp->addr, align);
continue;
}
//size+addr < addr ?除非size == 0
if ((size + addr) < addr)
goto out;
//中间的空隙可以容纳下size大小的vm.说明已经找到了这样的一个vm
if (size + addr <= (unsigned long)tmp->addr)
goto found;
//调整起始地址为vm的结束地址
addr = ALIGN(tmp->size + (unsigned long)tmp->addr, align);
//如果超出了范围
if (addr > end - size)
goto out;
}
found:
//找到了合适大小的空间,将area->addr赋值为addr,然后链入vmlist中
area->next = *p;
*p = area;
area->flags = flags;
area->addr = (void *)addr;
area->size = size;
area->pages = NULL;
area->nr_pages = 0;
area->phys_addr = 0;
write_unlock(&vmlist_lock);
return area;
out:
//没有找到合适大小的空间,出错返回
write_unlock(&vmlist_lock);
kfree(area);
if (printk_ratelimit())
printk(KERN_WARNING "allocation failed: out of vmalloc space - use vmalloc=
return NULL;
}
这段代码不是很复杂,在此不详细分析了.
remove_vm_area用来将相应的vm从vmlist中断开,使其表示的空间可以被利用
//addr:对应vm的超始地址
struct vm_struct *remove_vm_area(void *addr)
{
struct vm_struct **p, *tmp;
write_lock(&vmlist_lock);
//遍历vmlist.找到超始地址为addr的vm
for (p = &vmlist ; (tmp = *p) != NULL ;p = &tmp->next) {
if (tmp->addr == addr)
goto found;
}
write_unlock(&vmlist_lock);
return NULL;
found:
//断开tmp所对应的映射关系
unmap_vm_area(tmp);
//找到了这个vm,将其从vmlist上断开
*p = tmp->next;
write_unlock(&vmlist_lock);
return tmp;
}
unmap_vm_area用来断开vm所在线性地址所对应的映射关系.它的代码如下:
void unmap_vm_area(struct vm_struct *area)
{
//vm所对应的起始线性地址
unsigned long address = (unsigned long) area->addr;
//vm所对应的结束线性地址
unsigned long end = (address + area->size);
pgd_t *dir;
//起始地址所在的内核页目录项
dir = pgd_offset_k(address);
flush_cache_vunmap(address, end);
do {
//断开地址所对应的pmd映射
unmap_area_pmd(dir, address, end - address);
评论暂时关闭