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…...
零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?
一、核心优势:专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发,是一款收费低廉但功能全面的Windows NAS工具,主打“无学习成本部署” 。与其他NAS软件相比,其优势在于: 无需硬件改造:将任意W…...
2.Vue编写一个app
1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...
新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列,以便知晓哪些列包含有价值的数据,…...
Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?
Redis 的发布订阅(Pub/Sub)模式与专业的 MQ(Message Queue)如 Kafka、RabbitMQ 进行比较,核心的权衡点在于:简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...
NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合
在汽车智能化的汹涌浪潮中,车辆不再仅仅是传统的交通工具,而是逐步演变为高度智能的移动终端。这一转变的核心支撑,来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒(T-Box)方案:NXP S32K146 与…...
比较数据迁移后MySQL数据库和OceanBase数据仓库中的表
设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...
k8s从入门到放弃之HPA控制器
k8s从入门到放弃之HPA控制器 Kubernetes中的Horizontal Pod Autoscaler (HPA)控制器是一种用于自动扩展部署、副本集或复制控制器中Pod数量的机制。它可以根据观察到的CPU利用率(或其他自定义指标)来调整这些对象的规模,从而帮助应用程序在负…...
