复合页机制

前言

在从伙伴系统进行内存分配时有一个__GFP_COMP分配flag,该flag表示从伙伴系统分配的连续页帧为一个复合页。复合页就是将多个页帧进行组合,视作一个更大size的页。

复合页可以用于hugetlb,减少tlb中地址转化的次数,减少tlb miss几率,同时提高tlb的地址转化速度。slab分配器也会分配复合页用于管理。这里我对复合页的具体用途不展开详谈,先描述一下复合页在内核中是如何实现、分配以及释放的。

复合页的设计

复合页由连续的多个page构成,其中首个page称为head page,剩余的page都称作tail page。

复合页的设计

复合页的设计

所有的page的first_page字段都存放指向head page的指针(该图来自于《深入Linux内核架构》,图中用的是private指针和阅读的code中的实现略有区别),此外head page后的第一个page,即第一个tail page的lru.nextlru.prev分别存放释放复合页的函数指针以及复合页的分配阶。

head page的lru.nextlru.prev用于将复合页链入放入某个队列进行管理。

此外,所有的page都被标记上PG_compound,表明是复合页的一部分。

复合页的分配

复合页的分配和普通的伙伴系统内存分配相同,区别在于分配完成后prep_new_page针对复合页做了额外的初始化操作。

static int prep_new_page(struct page *page, int order, gfp_t gfp_flags)
{
    ...
    if (order && (gfp_flags & __GFP_COMP))
        prep_compound_page(page, order);
    ...
}

prep_compound_page中,set_compound_page_dtor会设置好析构函数用于后续释放复合页,存放位置为page[1].lru.nextset_compound_order设置复合页的分配阶,存放位置为page[1].lru.prev。可以看到析构函数和分配阶都是存放在复合页的第一个tail page中。

然后为head page以及每个tail page设置好PG_flag,__SetPageHeadhead page的flags会添加PG_compound,而__SetPageTail会对每一个tail page同时设置PG_reclaimPG_compound,用以区分head page和tail page。

static void prep_compound_page(struct page *page, unsigned long order)
{
    int i;
    int nr_pages = 1 << order;

    set_compound_page_dtor(page, free_compound_page);
    set_compound_order(page, order);
    __SetPageHead(page);
    for (i = 1; i < nr_pages; i++) {
        struct page *p = page + i;

        __SetPageTail(p);
        p->first_page = page;
    }
}

复合页的释放

首先释放复合页需要从page指针的first_page找到head page,然后从第一个tail page的lru.next上获取析构函数,lru.prev上获取分配阶,调用析构函数即可。

static void put_compound_page(struct page *page)
{
    page = compound_head(page);
    if (put_page_testzero(page)) {
        compound_page_dtor *dtor;

        dtor = get_compound_page_dtor(page);
        (*dtor)(page);
    }
}
posted @ 2023-10-08 10:28  ZouTaooo  阅读(198)  评论(0)    收藏  举报