Linux 内核复合页(compound page)原理分析

源码基于:Linux5.15

约定:

  • 芯片架构:ARM64
  • 内存架构:UMA
  • CONFIG_ARM64_VA_BITS:39
  • CONFIG_ARM64_PAGE_SHIFT:12
  • CONFIG_PGTABLE_LEVELS :3

1. 简介

复合页(Compound Page) 只是将两个或更多物理上连续的页面组合成一个单元,在许多方面可以将其视为单个更大的页面。它们最常用于创建大页面,在hugetlbfs或透明大页(transparent huge pages)子系统中使用,但它们也出现在其他场景中。复合页可以用作匿名内存或用作内核中的buffers;但是,它们不能出现在page cache中,page cache只能处理单个页面。

分配复合页面是调用 alloc_pages() 并设置 __GFP_COMP 分配标志和页帧数大于1, 即order至少为1。

2. 复合页初始化

mm/page_alloc.cstatic void prep_new_page(struct page *page, unsigned int order, gfp_t gfp_flags,unsigned int alloc_flags)
{post_alloc_hook(page, order, gfp_flags);if (order && (gfp_flags & __GFP_COMP))prep_compound_page(page, order);...
}
mm/page_alloc.cvoid prep_compound_page(struct page *page, unsigned int order)
{int i;int nr_pages = 1 << order;__SetPageHead(page);for (i = 1; i < nr_pages; i++) {struct page *p = page + i;p->mapping = TAIL_MAPPING;set_compound_head(p, page);}set_compound_page_dtor(page, COMPOUND_PAGE_DTOR);set_compound_order(page, order);atomic_set(compound_mapcount_ptr(page), -1);if (hpage_pincount_available(page))atomic_set(compound_pincount_ptr(page), 0);
}

复合页初始化可以用下图归纳:

注意:

  • 第一个page 中的flag 会标记 PG_head,标记此为复合页头页;

  • 之后所有page 都会配置两个属性:mapping 和 compound_head,并且通过 compound_head 确认是尾页还是头页;

  • 第二个page 中会存放更多复合页的信息,这也是为什么复合页的 order 至少为 1 的原因;

  • prep_compound_page() 函数最后会进行 hpage_pincount_available() 判断,这个特殊场景,需要order 至少为2;

3. 常用接口

3.1 compound_head()

include/linux/page-flags.hstatic inline unsigned long _compound_head(const struct page *page)
{unsigned long head = READ_ONCE(page->compound_head);if (unlikely(head & 1))return head - 1;return (unsigned long)page;
}#define compound_head(page)        ((typeof(page))_compound_head(page))

通过 page->compound_head 的最后一位是否设为 1 来判断该页是否为头页,如果不是头页,只需要减 1 即可获得头页地址。

3.2 PageCompound() 和 PageTail()

include/linux/page-flags.hstatic __always_inline int PageTail(struct page *page)
{return READ_ONCE(page->compound_head) & 1;
}static __always_inline int PageCompound(struct page *page)
{return test_bit(PG_head, &page->flags) || PageTail(page);
}

同上,通过 page->compound_head 的最后一位是否设为 1 来判断头页还是尾页。

3.3 destroy_compound_page()

inlucde/linux/mm.hstatic inline void destroy_compound_page(struct page *page)
{VM_BUG_ON_PAGE(page[1].compound_dtor >= NR_COMPOUND_DTORS, page);compound_page_dtors[page[1].compound_dtor](page);
}

该函数通过调用 page[1].compound_dtor 记录的析构函数来释放复合页。

在 prep_compound_page() 中将复合页的析构函数默认指向 COMPOUND_PAGE_DTOR,即 free_compound_page() 函数,如下:

3.3.1 free_compound_page()

mm/page_alloc.ccompound_page_dtor * const compound_page_dtors[NR_COMPOUND_DTORS] = {[NULL_COMPOUND_DTOR] = NULL,[COMPOUND_PAGE_DTOR] = free_compound_page,
#ifdef CONFIG_HUGETLB_PAGE[HUGETLB_PAGE_DTOR] = free_huge_page,
#endif
#ifdef CONFIG_TRANSPARENT_HUGEPAGE[TRANSHUGE_PAGE_DTOR] = free_transhuge_page,
#endif
};

对于COMPOUND_PAGE_DTOR 对应的是 free_compound_page() 函数。

mm/page_alloc.cvoid free_compound_page(struct page *page)
{mem_cgroup_uncharge(page);free_the_page(page, compound_order(page));
}

3.3.2 set_compound_page_dtro()

inlucde/linux/mm.hstatic inline void set_compound_page_dtor(struct page *page,enum compound_dtor_id compound_dtor)
{VM_BUG_ON_PAGE(compound_dtor >= NR_COMPOUND_DTORS, page);page[1].compound_dtor = compound_dtor;
}

通过set_compound_page_dtor() 函数页可以动态设置复合页的析构函数。

3.4 compound_order()

在 inlucde/linux/mm.h 中有很多关于复合页的使用:

inlucde/linux/mm.hstatic inline unsigned int compound_order(struct page *page)
{if (!PageHead(page))return 0;return page[1].compound_order;
}

通过该函数获取复合页的order 大小。

4. 使用

复合页的分配主要是通过 gfp flag:__GFP_COMP很多模块在申请内存时都会带上该标志。

例如,ion_page_pool_create():

drivers/staging/android/ion/heaps/ion_page_pool.cstruct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order)
{struct ion_page_pool *pool = kmalloc(sizeof(*pool), GFP_KERNEL);...pool->gfp_mask = gfp_mask | __GFP_COMP;pool->order = order;...return pool;
}

在后期申请内存池时,会带入 pool->gfp_mask 调用alloc_pages() 函数。

再例如,kmalloc_order():

mm/slab_common.cvoid *kmalloc_order(size_t size, gfp_t flags, unsigned int order)
{...flags |= __GFP_COMP;page = alloc_pages(flags, order);...return ret;
}

当通过 kmalloc() 申请的内存超过 8K 时,会直接从 buddy 中分配,默认就是复合页。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/133.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【笔试强训】DFS、优先队列、滑动窗口笔试题目!

文章目录 1. 单词搜索2. 除 2 操作3. dd 爱框框 1. 单词搜索 题目链接 解题思路&#xff1a; DFS (深度优先遍历)&#xff0c;用一个 pos 记录要匹配单词 word 的位置&#xff0c;每次与 pos 进行匹配判断&#xff08;这样做的好处是不用把答案存下来&#xff09; 注意细节…

算法:期望场景;鲁棒优化

部分代码 for i1:T stst[D_DGk(i)*min_P_DG<P_DGk(i)<D_DGk(i)*max_P_DG]; end for i2:T indicatorD_DGk(i)-D_DGk(i-1); rangei:min(T,iT_up-1); st st[D_DGk(range)>indicator]; end for i2:T indicatorD_DGk(i-1)-D_DGk(i); rangei:min(T…

时序分解 | Matlab实现TVF-EMD时变滤波器的经验模态分解信号分量可视化

时序分解 | Matlab实现TVF-EMD时变滤波器的经验模态分解信号分量可视化 目录 时序分解 | Matlab实现TVF-EMD时变滤波器的经验模态分解信号分量可视化效果一览基本介绍程序设计参考资料 效果一览 基本介绍 Matlab实现TVF-EMD(时变滤波器的经验模态分解)可直接替换 Matlab语言 1.…

IDEA远程调试debug

IDEA远程调试debug jar包启动脚本配置IDEA配置 通俗的说&#xff1a;本地有代码&#xff0c;服务器项目出现问题&#xff0c;环境的中间件配置不同&#xff0c;用idea远程调试&#xff0c;能快速定位问题&#xff0c;解决问题。 jar包启动脚本配置 jdk5-8写法 java -Xdebug -…

xftp、xshell连不上虚拟机解决方法

一、检查连接虚拟机ip看是否正确 查看虚拟机系统 IP ifconfig 二、检查虚拟机防火墙是否关闭 查看防火墙状态(ubuntu) sudo ufw status 关闭防火墙 sudo ufw disable 查看防火墙状态(centos) systemctl status firewalld.service 关闭防火墙 systemctl stop firewalld.se…

基于Python dlib的实时人脸识别,附源码

博主介绍&#xff1a;✌IT徐师兄、7年大厂程序员经历。全网粉丝15W、csdn博客专家、掘金/华为云//InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3…

【STM32】西南交大嵌入式系统设计实验:环境配置

把走过的坑记录一下&#xff0c;希望后来人避坑 No ST-Link device detected.问题解决 如果跟着指导书出现这个问题&#xff1a; 直接跳过这一步不用再更新固件&#xff0c;后面直接创建项目写程序就行了。 在keil里配置成用DAP_link即可。 详细的可以看这篇文章&#xff1a…

30. 【Android教程】吐司提示:Toast 的使用方法

在使用 Android 手机的时候&#xff0c;有没有遇到过如图中这种类型的消息提示&#xff1f; 这个在 Android 中被称为 Toast&#xff0c;用来短暂的展示一些简短的提示信息。相比弹窗来讲它对用户的打扰更小&#xff0c;在提示一段时间之后会自动消失&#xff0c;通常用来提示当…

糖尿病可能是一团虚火,肝肾同源,肝阴不足。

其实对于很多的糖尿病患者来说&#xff0c;他的问题本质可能是一团虚火&#xff0c;就拿前段时间我的门诊一个患者为例&#xff0c;之前患有高血压&#xff0c;总是眩晕烦躁&#xff0c;常常失眠&#xff0c;大概近四个月出现多饮、多尿怎么喝水也不解渴&#xff0c;经过检查确…

CSS导读 (CSS的三大特性 上)

&#xff08;大家好&#xff0c;今天我们将继续来学习CSS的相关知识&#xff0c;大家可以在评论区进行互动答疑哦~加油&#xff01;&#x1f495;&#xff09; 目录 五、CSS的三大特性 5.1 层叠性 5.2 继承性 5.2.1 行高的继承 5.3 优先级 小练习 五、CSS的三大特性 …

IDEA中SVN 的使用

文章目录 前言一、svn安装二、IDEA集成SVN总结 前言 svn可以老牌的代码仓库了 说实话svn还是和git无法相比的,毕竟git有本地仓库的概念,可以很好的处理冲突,然而svn是没有本地仓库的概念的,所以只能拉取别人的代码,然后处理冲突后,才能提交代码; 由于最近的工作换成了用svn仓…

2024香港Web3嘉年华:Web3的两大支柱是区块链和AI

2024香港Web3嘉年华&#xff1a;Web3的两大支柱是区块链和AI 4月9日&#xff0c;香港Web3嘉年华落下帷幕圆满收官。这场盛会延续了往年的火爆&#xff0c;保留了社交和打卡元素。大批加密货币从业者齐聚一堂&#xff0c;在多方碰撞中&#xff0c;共同探讨香港与加密的未来之路…

三种空间数据的聚合算法

原始数据分布 给老外做的Demo&#xff0c;所以是英文界面。 原始数据分布情况如下&#xff1a; geojson文本内容&#xff1a; 三种方法基本原理 三种聚合算法来做一个例子&#xff08;500条记录&#xff09;。 方法1&#xff1a;按Ol默认方法进行聚类&#xff0c;使用Open…

可见光相机曝光方式

可见光摄影中的曝光方式主要包括两种&#xff1a;卷帘曝光和全局曝光。它们之间的区别在于曝光过程中传感器或胶片感光部分的工作方式不同&#xff0c;这直接影响到图像捕获的效果和特性。 卷帘曝光&#xff08;Rolling Shutter&#xff09;&#xff1a; 工作原理&#xff1a;在…

对一个全局变量进行多线程并发 -- 或者 ++ 操作是否是安全的??是否是原子的??

1.结论&#xff1a; 不是安全的&#xff0c;不是原子的 2.原因&#xff1a; 2.1 不是原子性的原因&#xff1a; 一个线程将一个全局变量--&#xff08;减减&#xff09;时候&#xff0c;需要以下几个步骤 第一步&#xff1a;将全局变量读到cpu的寄存器中&#xff0c; 第二步…

3D目标检测实用技巧(三)- 生成虚拟点

一、引言 本次参考的是VirConv生成虚拟点的方法&#xff1a; VirConv/tools/PENet at master hailanyi/VirConv GitHubVirtual Sparse Convolution for Multimodal 3D Object Detection - VirConv/tools/PENet at master hailanyi/VirConvhttps://github.com/hailanyi/Vir…

腾讯文档推出智能白板创新品类,自研开物引擎支持全端应用

4月18日&#xff0c;“腾讯文档&#xff0c;打开想象”2024产品发布会在线上举办。腾讯社交协作产品部总经理、腾讯文档负责人鄢贤卿在会上&#xff0c;正式发布智能白板创新品类、双核编辑“开物引擎”、云加端解决方案等产品矩阵。他表示&#xff0c;在自研文档品类和自研双核…

ctfshow web入门 SQl注入web171--web179

从这里开始SQl建议大家去看这篇文章学习一下先 MySQl web171 法一联合查询 题目 $sql "select username,password from user where username !flag and id ".$_GET[id]." limit 1;";爆数据库名 -1 union select 1,database(),3 -- 爆表名 -1 union s…

数据结构 - 栈

概述 计算机科学中,stack是一种线性的数据结构,只能在其一段添加和移除数据. 习惯来说,这一端称之为栈顶,另一端不能操作数据的称之为栈底,就如同生活中的一摞书 先提供一个接口: public interface Stack <E>{/*** 向栈顶压入元素* param value -- 待压入值* returns…

✌粤嵌—2024/4/18—旋转链表✌

代码实现&#xff1a; 方法一&#xff1a;在原链表中找到旋转之后的首尾&#xff0c;改变指向 /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/ struct ListNode* rotateRight(struct ListNode *head, int k) {i…