linux 复合页( Compound Page )的介绍
1、复合页的定义:
复合页(Compound Page)就是将物理上连续的两个或多个页看成一个独立的大页,它可以用来创建hugetlbfs中使用的大页(hugepage), 也可以用来创建透明大页(transparent huge page)子系统。但是它不能用在页缓存(page cache)中,这是因为页缓存中管理的都是单个页。
2、复合页的分配及标记:
当__alloc_pages分配标志gfp_flags指定了__GFP_COMP,那么内核必须将这些页组合成复合页compound page。复合页的尺寸要远大于当前分页系统支持的页面大小。并且一定是2^order * PAGE_SIZE大小。复合页主要用在HugeTLB相关的代码。复合页的引入是因为随着计算机物理内存容量不断增大,4G以上几乎成了标配,几十G的内存也很常见,而操作系统仍然使用4KB大小页面的基本单位,显得有些滞后。当采用4KB大小的页面时,想像一下当应用程序分配2MB内存,并进行访问时,共有512个页面,操作系统会经历512次TLB miss和512次缺页中断后,才可以把这2M地址空间全部映射到物理内存上;然而如果使用2MB大小的compand页,那么只需要一次TLB miss和一次缺页中断。当页面分配函数使用GFP_COMP进行页面分配时,分配函数会为每一个增加标志PG_Compound,我们称复合页中的第一个4KB页面为head page,后面的所有page 为tail page。每个page的private保存一个指针,head page的private指向本身,tail page的private指向head page。
Page页中的flag标记用来识别复合页。在复合页中,打头的第一个普通页成为“head page”,用PG_head标记,而后面的所有页被称为“tail pages”,用PG_tail标记。在64位系统中,可以有多余的标记来表示复合页的页头和页尾;但是在32位系统中却没有那么多的标记,因此采用了一种复用其他标记的方案,即将复合页中的所有页都用PG_compound标记,然后,对于尾页同时也使用PG_reclaim标记,这是因为PG_reclaim只有在页缓存中会用到,而复合页根本就不会在页缓存中使用。
//32位系统中的标记实例:
1 #define PG_head_mask ((1L << PG_compound)) 2 #define PG_head_tail_mask ((1L << PG_compound) | (1L << PG_reclaim)) 3 4 static inline int PageHead(struct page *page) 5 { 6 return ((page->flags & PG_head_tail_mask) == PG_head_mask); 7 } 8 9 10 static inline int PageTail(struct page *page) 11 { 12 return ((page->flags & PG_head_tail_mask) == PG_head_tail_mask); 13 }
3、复合页的检测:
可以使用PageCompound函数来检测一个页是否是复合页,另外函数PageHead和函数PageTail用来检测一个页是否是页头或者页尾。在每个尾页的page结构体中都包含一个指向头页的指针 - first_page,可以使用compound_head函数获得。
4、复合页的释放:
那么当一个复合页不再被系统使用时,我们如何知道该复合页包含多少个普通页,又如何知道该复合页的析构函数(destructor)存在哪里呢?首先,人们可能会认为这些信息存在于头页的page结构体中,但是很不幸,在这个结构体中已经没有可用的空间了。因此,这些信息全部存储在第一个尾页的lru字段中,将该复合页的大小(order)首先强制转换为指针类型,然后存储在lru.prev中,将析构函数存储在lru.next中。
1 static void free_compound_page(struct page *page) 2 { 3 __free_pages_ok(page, compound_order(page)); 4 } 5 6 static inline int compound_order(struct page *page) 7 { 8 if (!PageHead(page)) 9 return 0; 10 return (unsigned long)page[1].lru.prev; 11 }
这里就解释了为什么复合页必须至少是两个页。在内核中生成了两个复合页的析构函数,默认情况下会调用free_compound_page来将所有的页返回给系统的页框分配器,而hugetlbfs子系统会调用free_huge_page来做一些统计并释放。使用复合页的最经典的一个例子就是THP(transparent huge page)
from: https://www.cnblogs.com/penglcool/p/13293558.html
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析