linux内存页块划分及位图存储机制
page_alloc.c - mm/page_alloc.c - Linux source code v5.4.285 - Bootlin Elixir Cross Referencer
一. 什么是页块(Pageblock)?
-
定义:页块是物理内存中的一个连续区域,由
2^pageblock_order
个物理页(Page)组成。 -
作用:页块是内存碎片管理的最小单位,用于跟踪内存区域的 迁移类型(Migratetype)(如可移动、不可移动等),优化内存分配和碎片整理。
1.1. pageblock_order
的默认值
-
常规配置:
#ifdef CONFIG_HUGETLB_PAGE_SIZE_VARIABLE#define pageblock_order HUGETLB_PAGE_ORDER #else#define pageblock_order (MAX_ORDER - 1) #endif
-
默认情况:若未启用巨型页(HugeTLB)的动态大小配置,
pageblock_order
通常设置为MAX_ORDER - 1
。 -
MAX_ORDER
:伙伴系统的最大分配阶数,通常为11
(对应2^10 = 1024
页,即4MB
,假设页大小为4KB
)。
-
-
结果:
-
默认页块大小为
2^(MAX_ORDER-1)
页(例如MAX_ORDER=11
时,页块为2^10 = 1024
页 =4MB
)。 -
每个页块在全局位图
pageblock_flags
中占用若干位(如3位用于迁移类型)。
-
1.2. 为什么选择 MAX_ORDER - 1
?
设计目标:
-
对齐伙伴系统:确保页块大小与伙伴系统的最大连续内存块(
MAX_ORDER
)对齐,避免跨页块分配。 -
减少碎片:较大的页块能更高效地隔离不可移动内存(如内核对象),减少长期内存碎片。
-
迁移类型管理:每个页块独立维护迁移类型,方便内存碎片整理时批量移动页面。
权衡:
-
内存粒度:页块越大,管理更粗粒度,可能浪费内存;页块越小,管理更细粒度,但元数据开销增加。
-
性能:较大的页块减少位图操作次数,提高效率。
二、什么是页块位图?
1. 核心作用与背景
pageblock_flags
是 Linux 内存管理中的一个关键数据结构,主要用于跟踪和管理 内存块(pageblock) 的特性。通过 pageblock_flags
,内核可以高效地记录每个内存块的属性,例如迁移类型、分配状态等,从而优化内存分配与回收策略。
2. 数据结构与存储方式
- 存储位置:
pageblock_flags
以位图(bitmap)形式存储在struct zone
结构体中(字段unsigned long *pageblock_flags
),每个内存块对应位图中的若干比特位。// https://elixir.bootlin.com/linux/v5.4.285/source/include/linux/mmzone.h#L448struct zone { #ifndef CONFIG_SPARSEMEM/** Flags for a pageblock_nr_pages block. See pageblock-flags.h.* In SPARSEMEM, this map is stored in struct mem_section*/unsigned long *pageblock_flags; // 指向位图的指针 #endif /* CONFIG_SPARSEMEM */ }
单个
pageblock_flags
元素可表示的页块和页的数量为:32位系统:32/4=8 个页块=8*1024页。
64位系统:64/4=16 个页块=16*1024页。
- 位图管理:每个内存块的标志位由
NR_PAGEBLOCK_BITS
定义,用于存储以下信息:- 迁移类型(如
MIGRATE_UNMOVABLE
、MIGRATE_MOVABLE
等)。 - 内存块的其他状态(如是否跳过内存碎片整理等)
#define PB_migratetype_bits 3 /* Bit indices that affect a whole block of pages */ enum pageblock_bits {PB_migrate,PB_migrate_end = PB_migrate + PB_migratetype_bits - 1,/* 3 bits required for migrate types */PB_migrate_skip,/* 1位标记是否跳过压缩(PB_migrate_skip),避免重复处理低效内存块 *//** Assume the bits will always align on a word. If this assumption* changes then get/set pageblock needs updating.*/NR_PAGEBLOCK_BITS };
- 迁移类型(如
3. 初始化
free_area_init_core -> setup_usemap
static void __ref setup_usemap(struct pglist_data *pgdat,struct zone *zone,unsigned long zone_start_pfn,unsigned long zonesize)
{unsigned long usemapsize = usemap_size(zone_start_pfn, zonesize);zone->pageblock_flags = NULL;if (usemapsize) {zone->pageblock_flags =memblock_alloc_node(usemapsize, SMP_CACHE_BYTES,pgdat->node_id);if (!zone->pageblock_flags)panic("Failed to allocate %ld bytes for zone %s pageblock flags on node %d\n",usemapsize, zone->name, pgdat->node_id);}
}
setup_usemap
的作用是为内存管理区(Zone)分配并初始化 pageblock_flags
位图,该位图用于跟踪每个内存块(Pageblock)的迁移类型(Migratetype)和其他状态。
参数说明
struct pglist_data *pgdat
: 指向 NUMA 节点的pglist_data
结构,描述节点的内存布局。struct zone *zone
: 目标内存管理区(如ZONE_NORMAL
、ZONE_DMA
)。unsigned long zone_start_pfn
: 该 Zone 的起始物理页帧号(Page Frame Number)。unsigned long zonesize
: 该 Zone 的总页数。
代码逻辑分解
1. 计算 pageblock_flags
位图大小
#define PB_migratetype_bits 3
/* Bit indices that affect a whole block of pages */
enum pageblock_bits {PB_migrate,PB_migrate_end = PB_migrate + PB_migratetype_bits - 1,/* 3 bits required for migrate types */PB_migrate_skip,/* 1位标记是否跳过压缩(PB_migrate_skip),避免重复处理低效内存块 *//** Assume the bits will always align on a word. If this assumption* changes then get/set pageblock needs updating.*/NR_PAGEBLOCK_BITS
};#define pageblock_nr_pages (1UL << pageblock_order)static unsigned long __init usemap_size(unsigned long zone_start_pfn, unsigned long zonesize)
{unsigned long usemapsize;zonesize += zone_start_pfn & (pageblock_nr_pages-1); // 步骤1:对齐修正usemapsize = roundup(zonesize, pageblock_nr_pages); // 步骤2:向上对齐到pageblock整数倍usemapsize = usemapsize >> pageblock_order; // 步骤3:计算pageblock数量usemapsize *= NR_PAGEBLOCK_BITS; // 步骤4:总位数 = 块数 × 4位usemapsize = roundup(usemapsize, 8 * sizeof(unsigned long));// 步骤5:位对齐到unsigned longreturn usemapsize / 8; // 步骤6:转换为字节
}
- **
usemap_size()
**: 计算位图所需内存大小(单位:字节)。- 参数:根据 Zone 的起始页帧(
zone_start_pfn
)和总页数(zonesize
)。 - 内部逻辑:
举例:某一个zone的start pfn = 0X1234;end pfn = 0X3600;zonesize = 0X23CC操作 目的 结果 计算对齐修正值 处理Zone起始地址未按pageblock对齐的情况(如Zone起始于一个pageblock中间),修正总页数以包含不完整的起始pageblock 修正值:zone_start_pfn & (pageblock_nr_pages-1)=0x234
zonesize += zone_start_pfn & (pageblock_nr_pages-1)=0x23CC+0x234=0x2600向上取整对齐 确保总页数是pageblock大小的整数倍(例如:1024页的倍数),避免部分pageblock无法被位图覆盖 usemapsize = roundup(zonesize, pageblock_nr_pages) = roundup(0x2600, 1024) = 0x2800页 计算pageblock数量 右移 pageblock_order
位(等价于除以pageblock_nr_pages
),得到Zone内完整的pageblock数量usemapsize = usemapsize >> pageblock_order; = 0x2800 >> 10 = 10 计算总位数 每个pageblock需要 NR_PAGEBLOCK_BITS
(4位)来存储状态,总位数 = pageblock数量 × 440 最终位图大小(字节) 总位数 / 8(1字节=8位) 40 / 8 = 5。(32bit系统上,需要两个 pageblock_flags
元素)
- 参数:根据 Zone 的起始页帧(
2. 分配 pageblock_flags
内存
if (usemapsize) {zone->pageblock_flags = memblock_alloc_node(usemapsize, SMP_CACHE_BYTES, pgdat->node_id);// ...
}
- 条件判断:仅在
usemapsize > 0
时分配内存(Zone 包含至少一个完整内存块)。 - 分配函数:
memblock_alloc_node
- 用途: 在内核启动早期(伙伴系统未初始化时),从
memblock
分配器分配内存。 - 参数:
usemapsize
: 分配的字节数。SMP_CACHE_BYTES
: 对齐到缓存行(通常 64 字节),避免伪共享(False Sharing)。pgdat->node_id
: 在指定 NUMA 节点上分配内存,确保 NUMA 亲和性。
- 用途: 在内核启动早期(伙伴系统未初始化时),从
- 结果:
zone->pageblock_flags
指向分配的位图内存。
三. 实际应用场景
3.1、内存碎片整理(Memory Compaction):
碎片整理器根据页块的迁移类型,将可移动页面(如用户态数据)迁移到其他页块,腾出连续内存。页块大小决定了迁移操作的最小单位。
3.1.1、设置迁移类型:set_pageblock_migratetype(struct page *page, int migratetype)
,用于在页释放时将其所属内存块的迁移类型标记为正确值
#define PB_migratetype_bits 3 /* Bit indices that affect a whole block of pages */ enum pageblock_bits {PB_migrate,PB_migrate_end = PB_migrate + PB_migratetype_bits - 1,/* 3 bits required for migrate types */PB_migrate_skip,/* If set the block is skipped by compaction *//** Assume the bits will always align on a word. If this assumption* changes then get/set pageblock needs updating.*/NR_PAGEBLOCK_BITS };#define set_pageblock_flags_group(page, flags, start_bitidx, end_bitidx) \set_pfnblock_flags_mask(page, flags, page_to_pfn(page), \end_bitidx, \(1 << (end_bitidx - start_bitidx + 1)) - 1)void set_pageblock_migratetype(struct page *page, int migratetype) {if (unlikely(page_group_by_mobility_disabled &&migratetype < MIGRATE_PCPTYPES))migratetype = MIGRATE_UNMOVABLE;set_pageblock_flags_group(page, (unsigned long)migratetype,PB_migrate, PB_migrate_end); }/*** set_pfnblock_flags_mask - Set the requested group of flags for a pageblock_nr_pages block of pages* @page: The page within the block of interest* @flags: The flags to set* @pfn: The target page frame number* @end_bitidx: The last bit of interest* @mask: mask of bits that the caller is interested in*/ void set_pfnblock_flags_mask(struct page *page, unsigned long flags,unsigned long pfn,unsigned long end_bitidx,unsigned long mask) {unsigned long *bitmap;unsigned long bitidx, word_bitidx;unsigned long old_word, word;BUILD_BUG_ON(NR_PAGEBLOCK_BITS != 4);BUILD_BUG_ON(MIGRATE_TYPES > (1 << PB_migratetype_bits));// 获取目标页块对应的位图指针及位偏移bitmap = get_pageblock_bitmap(page, pfn); //page_zone(page)->pageblock_flagsbitidx = pfn_to_bitidx(page, pfn);// 计算位图中的具体位置(原子操作)word_bitidx = bitidx / BITS_PER_LONG;bitidx &= (BITS_PER_LONG-1);VM_BUG_ON_PAGE(!zone_spans_pfn(page_zone(page), pfn), page);// 位操作:对齐掩码和标志值bitidx += end_bitidx;mask <<= (BITS_PER_LONG - bitidx - 1);flags <<= (BITS_PER_LONG - bitidx - 1);// 原子更新位图word = READ_ONCE(bitmap[word_bitidx]);for (;;) {old_word = cmpxchg(&bitmap[word_bitidx], word, (word & ~mask) | flags);if (word == old_word)break;word = old_word;} }static inline int pfn_to_bitidx(struct page *page, unsigned long pfn) {// 步骤1:计算对齐后的Zone起始PFN,并调整pfn为相对于该起始的偏移pfn = pfn - round_down(page_zone(page)->zone_start_pfn, pageblock_nr_pages);// 步骤2:将偏移转换为内存块索引,再乘以每块占用的位数,得到位索引return (pfn >> pageblock_order) * NR_PAGEBLOCK_BITS; }
迁移特性开关检查:
page_group_by_mobility_disabled
全局标志(由系统内存状态决定)若为真,表示禁用按迁移类型分组。此时强制将基础迁移类型(如MIGRATE_MOVABLE
)设为MIGRATE_UNMOVABLE
,避免碎片整理操作。
生成掩码:
(1 << (end_bitidx - start_bitidx + 1)) - 1
计算start_bitidx
到end_bitidx
的位宽(如3位),生成掩码0b111
pfn_to_bitidx:
将物理页帧号(PFN)转换为对应内存块(Pageblock)在位图(pageblock_flags
)中的起始位索引
示例:
- 参数:
zone_start_pfn = 0x1234
(未对齐到内存块大小)。pageblock_nr_pages = 1024
。pfn = 0x1500
(目标页 PFN)。- 计算过程:
- 对齐 Zone 起始 PFN:
round_down(0x1234, 1024) = 0x1000
。- PFN 偏移:
0x1500 - 0x1000 = 0x500
。- 内存块索引:
0x500 >> 10 = 1
(第 1 个内存块)。- 位索引:
1 * 4 = 4
。- 结果:
该页位于第 1 个内存块,其状态位在位图的第 4 位。
3.1.2、获取迁移类型:get_pageblock_migratetype(struct page *page)
,从页的元数据中提取所属内存块的迁移类型
3.1.3、初始化与校验:在系统启动时,内核会检查每个迁移类型的内存块是否达到最小数量(pageblock_nr_pages
),以决定是否启用迁移优化特性
3.2、连续内存分配器(CMA):
-
CMA 预留的连续内存以页块为单位管理,
pageblock_order
影响 CMA 区域的最小粒度。
3.3、巨型页(HugeTLB):
-
若启用动态巨型页大小(
CONFIG_HUGETLB_PAGE_SIZE_VARIABLE
),pageblock_order
可能与巨型页大小对齐。
3.4、内存热插拔:
-
动态调整内存区域时,通过
pageblock_flags
快速定位可迁移或可回收的内存块
page_alloc.c - mm/page_alloc.c - Linux source code v5.4.285 - Bootlin Elixir Cross Referencer
page_alloc.c - mm/page_alloc.c - Linux source code v5.4.285 - Bootlin Elixir Cross Referencer
相关文章:

linux内存页块划分及位图存储机制
page_alloc.c - mm/page_alloc.c - Linux source code v5.4.285 - Bootlin Elixir Cross Referencer 一. 什么是页块(Pageblock)? 定义:页块是物理内存中的一个连续区域,由 2^pageblock_order 个物理页(Pag…...

Vue 文件下载功能的跨域处理与前后端实现详解
在 Web 应用开发中,文件下载功能是常见需求。但由于跨域限制和认证机制的复杂性,实际开发中常遇到下载失败或权限错误等问题。本文将结合 Vue 前端和 Spring Boot 后端,详细介绍文件下载功能的实现与跨域问题的解决方案。 一、问题背景 在某…...

boost::beast websocket 实例
环境:ubuntu 1. 安装boost sudo apt install -y libboost-all-dev 2. Server端 #include <boost/asio.hpp> #include <boost/beast.hpp> #include <iostream> #include <thread>namespace beast boost::beast; // 从 Boost.Beast 中导…...

复试难度,西电卓越工程师学院(杭研院)考研录取情况
01、卓越工程师学院各个方向 02、24卓越工程师学院(杭研院)近三年复试分数线对比 PS:卓越工程师学院分为广研院、杭研院 分别有新一代电子信息技术、通信工程、集成电路工程、计算机技术、光学信息工程、网络信息安全、机械,这些…...

Rabbitmq--延迟消息
13.延迟消息 延迟消息:生产者发送消息时指定一个时间,消费者不会立刻收到消息,而是在指定时间之后才会收到消息 延迟任务:一定时间之后才会执行的任务 1.死信交换机 当一个队列中的某条消息满足下列情况之一时,就会…...

cocos creator使用mesh修改图片为圆形,减少使用mask,j减少drawcall,优化性能
cocos creator版本2.4.11 一个mask占用drawcall 3个以上,针对游戏中技能图标,cd,以及多玩家头像,是有很大优化空间 1.上代码,只适合单独图片的,不适合在图集中的图片 const { ccclass, property } cc._decorator;c…...

C++ Qt开发成长之路,从入门到企业级实战项目,保姆级学习路线
Qt 介绍 Qt是一个跨平台的C图形用户界面应用程序开发框架,最初由挪威的Trolltech公司开发,后来被诺基亚收购,现在由Qt公司维护。它提供了丰富的工具和类库,使开发者能够轻松地创建各种类型的应用程序,包括桌面应用、移…...

JavaWeb后端基础(7)AOP
AOP是Spring框架的核心之一,那什么是AOP?AOP:Aspect Oriented Programming(面向切面编程、面向方面编程),其实说白了,面向切面编程就是面向特定方法编程。AOP是一种思想,而在Spring框…...

Uniapp实现地图获取定位功能
摘要:本文将手把手教你如何在Uniapp项目中集成地图功能、实现定位获取,并解决微信小程序、APP、H5三端的兼容性问题🚀🚀🚀 一、环境准备 地图平台选择 微信小程序:腾讯地图(强制使用)…...

批量将 Excel 转换 PDF/Word/CSV以及图片等其它格式
Excel 格式转换是我们工作过程当中非常常见的一个需求,我们通常需要将 Excel 转换为其他各种各样的格式。比如将 Excel 转换为 PDF、比如说将 Excel 转换为 Word、再比如说将 Excel文档转换为图片等等。 这些操作对我们来讲都不难,因为我们通过 Office 都…...
Flutter:StatelessWidget vs StatefulWidget 深度解析
目录 1. 引言 2. StatelessWidget(无状态组件) 2.1 定义与特点 2.2 代码示例 3. StatefulWidget(有状态组件) 3.1 定义与特点 3.2 代码示例 4. StatelessWidget vs StatefulWidget 对比 5. StatefulWidget 生命周期 5.1…...

Stream流学习
Stream流 把数据放进stream流水线,对数据进行一系列操作(中间方法),最后封装(终结方法)。 Stream.of()允许传入任何参数 常见中间方法 可以对数据进行链式(流水线)操作,但…...

多视图几何--恢复相机位姿/内参的几种方法
恢复相机位姿的几种方法 1分解投影矩阵 1.1投影矩阵分解为相机内外参矩阵的完整解析 投影矩阵(Projection Matrix)是计算机视觉中将三维世界点映射到二维像素坐标的核心工具,其本质是相机内参矩阵(Intrinsic Matrix)…...

[数据结构]堆详解
目录 一、堆的概念及结构 二、堆的实现 1.堆的定义 2堆的初始化 3堆的插入 编辑 4.堆的删除 5堆的其他操作 6代码合集 三、堆的应用 (一)堆排序(重点) (二)TOP-K问题 一、堆的概念及结构 堆的…...

领域驱动设计(DDD)与MVC架构:理念对比与架构选择
领域驱动设计(DDD)与MVC架构:理念对比与架构选择 一、架构之争的本质:业务复杂度驱动技术演进 在软件开发领域,没有银弹式的完美架构,只有适合当前业务场景的合理选择。MVC与DDD的区别本质上是业务复杂度与…...

牛客周赛:84:B:JAVA
链接:登录—专业IT笔试面试备考平台_牛客网 来源:牛客网 题目描述 import java.util.ArrayList; import java.util.Arrays; import java.util.Scanner; public class Main {public static void main(String[] args) {Scanner scanner new Scanner(S…...

【理想解法学习笔记】
目录 理想解法原理简介算法步骤属性值规范化方法代码示例 理想解法 原理简介 TOPSIS(Technique for Order Preference by Simi larity to IdealSolution)法是一种逼近理想解的排序方法。其基本的处理思路是:首先建立初始化决策矩阵,而后基于规范化后的初…...

CI/CD—Jenkins配置一次完整的jar自动化发布流程
背景: 实现设想: 要创建自动化发布,需要准备一台测试服务器提前安装好java运行所需的环境,JDK版本最好和Windows开发机器上的版本一致,在Jenkins上配置将构建好的jar上传到测试服务器上,测试服务器自动启动…...

Magento2根据图片文件包导入产品图片
图片包给的图片文件是子产品的图片,如下图:A104255是主产品的sku <?php/*** 根据图片包导入产品图片,包含子产品和主产品* 子产品是作为主图,主产品是作为附加图片*/use Magento\Framework\App\Bootstrap;include(../app/boot…...

从零开始的python学习(五)P71+P72+P73+P74
本文章记录观看B站python教程学习笔记和实践感悟,视频链接:【花了2万多买的Python教程全套,现在分享给大家,入门到精通(Python全栈开发教程)】 https://www.bilibili.com/video/BV1wD4y1o7AS/?p6&share_sourcecopy_web&v…...

OpenHarmony5.0分布式系统源码实现分析—软总线
一、引言 OpenHarmony 作为一款面向万物互联的操作系统,其分布式软总线(Distributed SoftBus)是实现设备间高效通信和协同的核心技术之一。分布式软总线通过构建一个虚拟的总线网络,使得不同设备能够无缝连接、通信和协同工作。本…...

基于SpringBoot实现旅游酒店平台功能六
一、前言介绍: 1.1 项目摘要 随着社会的快速发展和人民生活水平的不断提高,旅游已经成为人们休闲娱乐的重要方式之一。人们越来越注重生活的品质和精神文化的追求,旅游需求呈现出爆发式增长。这种增长不仅体现在旅游人数的增加上࿰…...

代码随想录算法训练营第六十一天 | 108. 冗余连接 109. 冗余连接II
108. 冗余连接 题目链接:KamaCoder 文档讲解:代码随想录 状态:AC Java代码: import java.util.*;class Main {public static int[] father;public static void main(String[] args) {Scanner scan new Scanner(System.in);int n…...

RoboVQA:机器人多模态长范围推理
23 年 11 月来自 Google Deepmind 的论文“RoboVQA: Multimodal Long-Horizon Reasoning for Robotics”。 本文提出一种可扩展、自下而上且本质多样化的数据收集方案,该方案可用于长期和中期的高级推理,与传统的狭窄自上而下的逐步收集相比,…...

TCP/IP原理详细解析
前言 TCP/IP是一种面向连接,可靠的传输,传输数据大小无限制的。通常情况下,系统与系统之间的http连接需要三次握手和四次挥手,这个执行过程会产生等待时间。这方面在日常开发时需要注意一下。 TCP/IP 是互联网的核心协议族&…...

Microsof Visual Studio Code 安装教程(中文设置)
VS Code 是一个免费的代码编辑器,可在 macOS、Linux 和 Windows作系统上运行。启动和运行 VS Code 既快速又简单。VS Code(全称 Visual Studio Code)是一款由Microsoft 推出的免费、开源、跨平台的代码编辑器,拥有强大的功能和灵活…...

python爬虫:Android自动化工具Auto.js的详细使用
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 1. Auto.js 简介2. 安装与配置2.1 安装 Auto.js2.2 安装 Python 环境2.3 安装 ADB 工具3. Python 与 Auto.js 结合3.1 通过 ADB 执行 Auto.js 脚本3.2 通过 Python 控制 Auto.js3.3 通过 Python 与 Auto.js 交互4. 常用…...

Unity DOTS从入门到精通之 自定义Authoring类
文章目录 前言安装 DOTS 包什么是Authoring1. 实体组件2. Authoring类 前言 DOTS(面向数据的技术堆栈)是一套由 Unity 提供支持的技术,用于提供高性能游戏开发解决方案,特别适合需要处理大量数据的游戏,例如大型开放世…...

linux 软件安装(上)
一、基础环境准备 1.1、安装VM 1.2、在VM上导入linux iso镜像,装好linux系统 华为centos镜像下载地址 https://mirrors.huaweicloud.com/centos/ https://mirrors.huaweicloud.com/centos/7.9.2009/isos/x86_64/ 网易centos镜像下载地址 htt…...

php虚拟站点提示No input file specified时的问题及权限处理方法
访问站点,提示如下 No input file specified. 可能是文件权限有问题,也可能是“.user.ini”文件路径没有配置对,最简单的办法就是直接将它删除掉,还有就是将它设置正确 #配置成自己服务器上正确的路径 open_basedir/mnt/qiy/te…...