> 文档中心 > linux内核源码分析之伙伴系统(二)

linux内核源码分析之伙伴系统(二)

目录

分配器API

如何分配页面

分配一个页面情况

分配多个页面情况


页分配器API

        物理内存管理伙伴系统只能分配2的整数幂个页,即2 的order幂次页

申请页

  • alloc_pages(mask, order)分配2 的order幂次页并返回一个struct page的实例,表示分配的内存块的起始页。alloc_page(mask)是前者在order = 0情况下的简化形式,只分配一页。
  • get_zeroed_page(mask)分配一页并返回一个page实例,页对应的内存填充0(所有其他函数,分配之后页的内容是未定义的)。
  • __get_free_pages(mask, order)和__get_free_page(mask)的工作方式与上述函数相同,但返回分配内存块的虚拟地址,而不是page实例。
  • get_dma_pages(gfp_mask, order)用来获得适用于DMA的页。

释放页

  • free_page(struct page *)和free_pages(struct page *, order)用于将一个或2order页返 回给内存管理子系统。内存区的起始地址由指向该内存区的第一个page实例的指针表示。
  • __free_page(addr)和__free_pages(addr, order)的语义类似于前两个函数,但在表示需要释放的内存区时,使用了虚拟内存地址而不是page实例

如何分配页面

        实际完成分配任务的是 rmqueue 函数

static inlinestruct page *rmqueue(struct zone *preferred_zone,struct zone *zone, unsigned int order,gfp_t gfp_flags, unsigned int alloc_flags,int migratetype){unsigned long flags;struct page *page;if (likely(order == 0)) {//order=0 是一个页面,从pclist中分配page = rmqueue_pcplist(preferred_zone, zone, gfp_flags,migratetype, alloc_flags);goto out;}WARN_ON_ONCE((gfp_flags & __GFP_NOFAIL) && (order > 1));//加锁关中断spin_lock_irqsave(&zone->lock, flags);do {page = NULL;if (alloc_flags & ALLOC_HARDER) {//多个内存页面从free_area中分配page = __rmqueue_smallest(zone, order, MIGRATE_HIGHATOMIC);if (page)trace_mm_page_alloc_zone_locked(page, order, migratetype);}if (!page)    //最后调用__rmqueue_smallest函数page = __rmqueue(zone, order, migratetype, alloc_flags);} while (page && check_new_pages(page, order));spin_unlock(&zone->lock);if (!page)goto failed;...local_irq_restore(flags);out:    ...failed:local_irq_restore(flags);return NULL;}

分配一个页面情况

/* Lock and remove page from the per-cpu list */static struct page *rmqueue_pcplist(struct zone *preferred_zone,struct zone *zone, gfp_t gfp_flags,int migratetype, unsigned int alloc_flags){struct per_cpu_pages *pcp;struct list_head *list;struct page *page;unsigned long flags;    //关中断local_irq_save(flags);//获取当前CPU下的pcppcp = &this_cpu_ptr(zone->pageset)->pcp;//获取pcp下迁移的list链表list = &pcp->lists[migratetype];//摘取list上的pages结构page = __rmqueue_pcplist(zone,  migratetype, alloc_flags, pcp, list);if (page) {__count_zid_vm_events(PGALLOC, page_zonenum(page), 1);zone_statistics(preferred_zone, zone);}//开中断local_irq_restore(flags);return page;}

static struct page *__rmqueue_pcplist(struct zone *zone, int migratetype,unsigned int alloc_flags,struct per_cpu_pages *pcp,struct list_head *list){struct page *page;do {if (list_empty(list)) {//如果list为空,就从这个内存区域中分配一部分页面到pcp中来pcp->count += rmqueue_bulk(zone, 0,pcp->batch, list,migratetype, alloc_flags);if (unlikely(list_empty(list)))return NULL;}//获取list上第一个page结构page = list_first_entry(list, struct page, lru);list_del(&page->lru);//减少pcp页面计数pcp->count--;} while (check_new_pcp(page));return page;}

分配多个页面情况

调用 __rmqueue_smallest 函数,从 free_area 数组中分配。

static __always_inlinestruct page *__rmqueue_smallest(struct zone *zone, unsigned int order,int migratetype){unsigned int current_order;struct free_area *area;struct page *page;/* Find a page of the appropriate size in the preferred list */for (current_order = order; current_order free_area[current_order]);//获取free_area中对应migratetype为下标的free_list中的pagepage = get_page_from_free_area(area, migratetype);if (!page)continue;//拖链pagedel_page_from_free_area(page, area);//分割伙伴expand(zone, page, order, current_order, area, migratetype);set_pcppage_migratetype(page, migratetype);return page;}return NULL;}

        首先要取得 current_order 对应的 free_area 区中 page,若没有,就继续增加current_order,直到最大的 MAX_ORDER。要是得到一组连续 page 的首地址,就对其脱链,然后调用 expand 函数分割伙伴。

static inline void expand(struct zone *zone, struct page *page,int low, int high, struct free_area *area,int migratetype){unsigned long size = 1 < low) {area--;high--;size >>= 1;//每次循环左移一位VM_BUG_ON_PAGE(bad_range(zone, &page[size]), &page[size]);//标记为保护页,当其伙伴被释放时,允许合并if (set_page_guard(zone, &page[size], high, migratetype))continue;//把另一半pages加入对应的free_area中add_to_free_area(&page[size], area, migratetype);//设置伙伴set_page_order(&page[size], high);}}

参考

《深入linux内核架构》

剖析Linux内核物理内存管理-大学生教程-腾讯课堂