目录
1 怎样通过object地址获取其对应的struct slab?
2 struct page、struct folio和struct slab类型之间转换,怎么保证内部关键数据的传递?
3 怎样判断一个内存空间是属于slab、page管理?
4 struct page 结构中 __mapcount 和 page_type的理解
近期在解读Linux slub内存分配管理器相关代码,随着代码的不断深入会不断地自我提出新的疑问点,自己带着疑问又再次走查代码解答自我的疑问。这篇便是对于解读Slub内存时的自我疑问解。
参考代码:Linux-6.10
1 怎样通过object地址获取其对应的struct slab?
代码中有 slab =folio_slab(fvirt_to_folio(object)) 逻辑,也就是通过object获取其在内存中对应的page结构,在把page结构转换为folio结构,最后将folio结构转化为slab结构。也就该object对应的page结构空间,也是slab结构所在的空间。
通过slab分配逻辑也可有得出此结论。slab分配函数alloc_slab_page调用alloc_pages_node函数分配slab空间,alloc_pages_node函数返回值为分配空间对应的page结构(其实是通过获取可用的page结构来确定可用内存),将获取的struct page结构转换为struct folio,再通过folio_slab()将folio转换为struct slab,所以slab结构的空间就是page结构空间。
2 struct page、struct folio和struct slab类型之间转换,怎么保证内部关键数据的传递?
三个结构不仅共用内存空间,而且结构中部分关键元素在各自结构中偏移量也相同。如下系统编译时,会计算部分关键元素在struct slab、struct folio的偏移量,将此偏移量和strcut page结构中关键元素的偏移量进行对比。
struct slab中 __page_flags 、__page_refcount 和struct page中的flags、_refcount 偏移量进行对比。
文件路径:mm/slab.h//static_asserts 编译静态判断函数,如果条件不满足则编译报错。
#define SLAB_MATCH(pg, sl) \
static_assert(offsetof(struct page, pg) == offsetof(struct slab, sl)) //在编译过程判定flag在strcut page中偏移量是否和__page_flags 元素在struct slab中的偏移量是否一致,如果不一致则编译报错。
SLAB_MATCH(flags, __page_flags); //判定_refcount在struct page中的偏移量是否和 __page_refcount在struct page中偏移量相同
SLAB_MATCH(_refcount, __page_refcount);
struct folio中flags、_mapcount、refcount和struct page中flags、_mapcount、_refcount元素偏移量对比。
文件路径:include/linux/mm_type.h#define FOLIO_MATCH(pg, fl) \
static_assert(offsetof(struct page, pg) == offsetof(struct folio, fl))FOLIO_MATCH(flags, flags);
FOLIO_MATCH(_mapcount, _mapcount);
FOLIO_MATCH(_refcount, _refcount);
如果关键元素的偏移量不一致,则在编译过程直接报错。如果一致时,无论结构怎么转换在不重新赋值给关键元素时,关键元素在各结构中值一致。例如将page结构转换为slab结构、在不覆盖flags元素空间时,page->flags 和 slab->_page_flags值相同。如此变保证了关键元素值在不同结构间的传递。
3 怎样判断一个内存空间是属于slab、page管理?
上一节中可以推断出Linux内核中struct slab、和 struct folio、strcut page存在共用空间情况,哪怎样确认这个空间是属于哪个结构?或者任意一内存地址空间是属于slab还是page buddy内存管理器?
内核提供一个判断函数 static inline bool PageSlab(const struct page *page),该函数展开后如下:
static inline bool PageSlab(const struct page *page)
{struct folio = page_folio(page);return ((folio->page.page_type & (PAGE_TYPE_BASE | PG_slab)) == PAGE_TYPE_BASE);
}
从逻辑看当pag_type中不存在PG_slab标识时,则page属于slab结构。有些反常规,常规情况会认为需要page_type中有PG_slab对应标识时才会认为该pag属于slab结构。通过代码进一步确认page_type变量来历,没有发现太多关于page_type赋值和初始化地方。但是通过struct page结构(如下),_mapcount 和 page_type共用4字节储存单元,故对于_mapcount赋值则等同于操作pagetype。
Struct page {…union { /* This union is 4 bytes in size. *//** If the page can be mapped to userspace, encodes the number* of times this page is referenced by a page table.*/atomic_t _mapcount;/** If the page is neither PageSlab nor mappable to userspace,* the value stored here may help determine what this page* is used for. See page-flags.h for a list of page types* which are currently stored here.*/unsigned int page_type;};
…}
从__init_single_page - >page_mapcount_reset函数可以获取_mapcount = -1 即0xFFFF FFFF,则page_type 初始值也等于0xFFFF FFFF。如果该page要给slab用,则需要将PG_slab设置到page_type ,设置后page_type值为 0xFFFF EFFFF。当page已经为slab结构时,再去通过PageSlab()函数值进行类型判断其结果为true,则表明了此结构为slab结构,该结构对应的内存空间由slab分配器进行管理。page buddy及其他类型的判断也同此逻辑。
static inline void page_mapcount_reset(struct page *page)
{atomic_set(&(page)->_mapcount, -1);}
当从page buddy获取到page给slab时,会调用__folio_set_slab函数对page.page_type进行PG_slab标识,表示该空间对应的slab结构,该空间由slab分配器进行管理。
slab page分配函数调用:alloc_slab_page -> __folio_set_slab
4 struct page 结构中 __mapcount 和 page_type的理解
前文提到在struct page结构中_mapcount 和 page_type 共用4字节内存单元(同一union单元),初始化值相同为0xFFFF FFFF (-1,见page_mapcount_reset函数)。_mapcount代表该内存空间被映射用户空间的引用次数(一个物理page内存可能被映射到不同用户内存空间),page_type表示该page的类型即属于buddy、slab、table等内存管理器或者页表专用。实际使用该空间只能有个意义:要么表示用户空间引用次数、要么标识page类型。具体表示什么意义可根据该空间值的范围确认: 0xFFFF FFFF - 0xFFFF FF80 (PAGE_MAPCOUNT_RESERVE = -128 =0xFFFF FFF80)时表示该page被映射到用户空间的次数,当小于PAGE_MAPCOUNT_RE-SERVE(0xFFFF FF80)时表示该page的类型。
如下两个函数可以佐证如上逻辑:
A.page_type_has_type函数用于判定是否有page的类型,当base_type小于 PAGE_MAPCOUN-T_RESERVE(0xFFFF FF80)是被认为有page类型。
static inline int page_type_has_type(unsigned int page_type)
{return (int)page_type < PAGE_MAPCOUNT_RESERVE;}
B.page_mapcount函数获取page的到用户空间映射的应用次数,当mapcount小于PAGE_MAP-COUNT_RESERVE(0xFFFF FF80)时返回0被认为没有引用,也就是该空间被设置了Page类型(enum pagetype)。
static inline int page_mapcount(struct page *page)
{int mapcount = atomic_read(&page->_mapcount) + 1;/* Handle page_has_type() pages */if (mapcount < PAGE_MAPCOUNT_RESERVE + 1)mapcount = 0;if (unlikely(PageCompound(page)))mapcount += folio_entire_mapcount(page_folio(page));return mapcount;
}
带着疑问走读代码总会有不一样的收货,知道自己的理解依然不够全面、甚至有误但带着问题前行会让前行更有目的,也让自己更多注重对于细节的理解。
继续前行,日拱一卒!