伙伴管理算法初衷是解决外部碎片问题,而slab算法则是用于解决内部碎片问题,但是内存使用的得不合理终究会产生碎片。碎片问题产生后,申请大块连续内存将可能持续失败,但是实际上内存的空闲空间却是足够的。这时候就引入了不连续页面管理算法,即我们常用的vmalloc申请分配的内存空间,它主要是用于将不连续的页面,通过内存映射到连续的虚拟地址空间中,提供给申请者使用,由此实现内存的高利用。
按照分析管理,从该管理算法的初始化入手。vmalloc_init()为vmalloc不连续内存管理初始化函数。具体实现:
- 【file:/mm/vmalloc.c】
- void __init vmalloc_init(void)
- {
- struct vmap_area *va;
- struct vm_struct *tmp;
- int i;
-
- for_each_possible_cpu(i) {
- struct vmap_block_queue *vbq;
- struct vfree_deferred *p;
-
- vbq = &per_cpu(vmap_block_queue, i);
- spin_lock_init(&vbq->lock);
- INIT_LIST_HEAD(&vbq->free);
- p = &per_cpu(vfree_deferred, i);
- init_llist_head(&p->list);
- INIT_WORK(&p->wq, free_work);
- }
-
- /* Import existing vmlist entries. */
- for (tmp = vmlist; tmp; tmp = tmp->next) {
- va = kzalloc(sizeof(struct vmap_area), GFP_NOWAIT);
- va->flags = VM_VM_AREA;
- va->va_start = (unsigned long)tmp->addr;
- va->va_end = va->va_start + tmp->size;
- va->vm = tmp;
- __insert_vmap_area(va);
- }
-
- vmap_area_pcpu_hole = VMALLOC_END;
-
- vmap_initialized = true;
- }
其中__insert_vmap_area()的实现值得一读,这里面有几个比较关键的数据在这里。
- 【file:/mm/vmalloc.c】
- static void __insert_vmap_area(struct vmap_area *va)
- {
- struct rb_node **p = &vmap_area_root.rb_node;
- struct rb_node *parent = NULL;
- struct rb_node *tmp;
-
- while (*p) {
- struct vmap_area *tmp_va;
-
- parent = *p;
- tmp_va = rb_entry(parent, struct vmap_area, rb_node);
- if (va->va_start < tmp_va->va_end)
- p = &(*p)->rb_left;
- else if (va->va_end > tmp_va->va_start)
- p = &(*p)->rb_right;
- else
- BUG();
- }
-
- rb_link_node(&va->rb_node, parent, p);
- rb_insert_color(&va->rb_node, &vmap_area_root);
-
- /* address-sort this list */
- tmp = rb_prev(&va->rb_node);
- if (tmp) {
- struct vmap_area *prev;
- prev = rb_entry(tmp, struct vmap_area, rb_node);
- list_add_rcu(&va->list, &prev->list);
- } else
- list_add_rcu(&va->list, &vmap_area_list);
- }
该函数主要动作先是遍历vmap_area_root红黑树(这是一棵根据非连续内存地址排序的红黑树),查找合适的节点位置,然后rb_insert_color()插入到红黑树中,最后则是查找插入的内存块管理树的父节点,有则插入到该节点链表的后面位置,否则作为链表头插入到vmap_area_list链表中(该链表同样是根据地址排序的)。