//分配一个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= to increase size.\n");

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);


相关内容