alloc_pages的实现浅析

alloc_pages的使用

struct page *alloc_pages(gft_t gfp, unsigned int order)

alloc_pages定义于 inux/gfp.h 中. 该函数用于分配2^order个 连续 的物理页. 分配失败返回NULL。

alloc_pages的调用链

这里写图片描述

主功能函数

static struct page *
get_page_from_freelist(gfp_t gfp_mask, nodemask_t *nodemask, unsigned int order, struct zonelist *zonelist, int high_zoneidx, int alloc_flags,  struct zone *preferred_zone, int migratetype)

static inline
struct page *buffered_rmqueue(struct zone *preferred_zone,
            struct zone *zone, int order, gfp_t gfp_flags,
            int migratetype)

static struct page *__rmqueue_smallest(struct zone *zone, unsigned int order,
                        int migratetype)

static int rmqueue_bulk(struct zone *zone, unsigned int order, 
            unsigned long count, struct list_head *list,
            int migratetype, int cold)

get_page_from_freelist() 遍历整个 zonelist, 如果找到一个watermark满足要求的zone, 就在这个zone上调用 buffered_rmqueue.

struct page *buffered_rmqueue(struct zone *preferred_zone,
            struct zone *zone, int order, gfp_t gfp_flags,
            int migratetype)
{
    unsigned long flags;
    struct page *page;
    //是否使用cold cache
    int cold = !!(gfp_flags & __GFP_COLD);

again:
    //如果要请求页的数量为1
    if (likely(order == 0)) {
        struct per_cpu_pages *pcp;
        struct list_head *list;
        //把当前中断状态保存到flags中,然后禁用当前处理器上的中断发送。flags 被直接传递, 而不是通过指针来传递。
        local_irq_save(flags);
        //获取本地CPU上的per_cpu_pages结构
        pcp = &this_cpu_ptr(zone->pageset)->pcp;
        //获取高速缓冲中页框描述符链表的头指针   
        list = &pcp->lists[migratetype];
        //如果链表为空,则向高速缓冲中添加页框
        if (list_empty(list)) {
            pcp->count += rmqueue_bulk(zone, 0,
                    pcp->batch, list,
                    migratetype, cold);
            if (unlikely(list_empty(list)))
                goto failed;
        }

        if (cold)
        // 如果从cold高速缓存中请求页
            page = list_entry(list->prev, struct page, lru);
        else
        // 
            page = list_entry(list->next, struct page, lru);
        //从LRU链表中删除该页   
        list_del(&page->lru);
        //缓存中的页框数减1
        pcp->count--;
    } else {
        //保存当前中断状态到flags, 并请求zonelock
        spin_lock_irqsave(&zone->lock, flags);
        page = __rmqueue(zone, order, migratetype);
        spin_unlock(&zone->lock);
        if (!page)
            goto failed;
        __mod_zone_page_state(zone, NR_FREE_PAGES, -(1 << order));
    }

    __count_zone_vm_events(PGALLOC, zone, 1 << order);
    zone_statistics(preferred_zone, zone);
    local_irq_restore(flags);

    VM_BUG_ON(bad_range(zone, page));
    if (prep_new_page(page, order, gfp_flags))
        goto again;
    return page;

failed:
    local_irq_restore(flags);
    return NULL;
}
static inline
struct page *__rmqueue_smallest(struct zone *zone, unsigned int order,
                        int migratetype)
{
    unsigned int current_order;
    struct free_area * area;
    struct page *page;

    //从指定的order开始,寻找一个指向非空free list的free area
    //如果指定的order对应的free area满足要求,则从中返回一个页块
    //否则使用expand进一步处理
    for (current_order = order; current_order < MAX_ORDER; ++current_order) {
        //从指定内存区间中获取的起始地址
        area = &(zone->free_area[current_order]);
        //判断该空闲区间的指定迁移类型的空闲列表是否为空
        //为空查找下一个块
        if (list_empty(&area->free_list[migratetype]))
            continue;

        page = list_entry(area->free_list[migratetype].next,
                            struct page, lru);
        //将该页从LRU链表中删除
        list_del(&page->lru);
        rmv_page_order(page);
        //将内存区间的可用页面数减1
        area->nr_free--;
        //返回的页块大于请求的页块,将页块的剩余页框分配到其他order的free area
        expand(zone, page, order, current_order, area, migratetype);
        return page;
    }
    return NULL;
}

假设我们请求分配一个order=2,大小为4的页块,order的最大值是5。 前4个free area都为空。我们从order=5的area得到一个大小为32的页块。所以我们order5的页块分配到order=3和order=4的页块。这时,order2 order3 order4分别得到了大小为4,8和16的页块。
就这样一个32大小的页块被分给了low以上的free area。
这里写图片描述

static inline void expand(struct zone *zone, struct page *page,
    int low, int high, struct free_area *area,
    int migratetype)
{
    //high为实际分配到的页块的order,此处得到页块的大小
    unsigned long size = 1 << high;

    while (high > low) {
        area--;
        high--;
        //低一级页块的大小为上一级页块大小的1/2
        size >>= 1;
        VM_BUG_ON(bad_range(zone, &page[size]));
        list_add(&page[size].lru, &area->free_list[migratetype]);
        //area的可用页块数加一
        area->nr_free++;
        set_page_order(&page[size], high);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值