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…...
《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...
定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...
用docker来安装部署freeswitch记录
今天刚才测试一个callcenter的项目,所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...
Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
文章目录 Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket(服务端和客户端都要)2. 绑定本地地址和端口&#x…...
人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式
今天是关于AI如何在教学中增强学生的学习体验,我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育,这并非炒作,而是已经发生的巨大变革。教育机构和教育者不能忽视它,试图简单地禁止学生使…...
处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的
修改bug思路: 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑:async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...
LLMs 系列实操科普(1)
写在前面: 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容,原视频时长 ~130 分钟,以实操演示主流的一些 LLMs 的使用,由于涉及到实操,实际上并不适合以文字整理,但还是决定尽量整理一份笔…...
nnUNet V2修改网络——暴力替换网络为UNet++
更换前,要用nnUNet V2跑通所用数据集,证明nnUNet V2、数据集、运行环境等没有问题 阅读nnU-Net V2 的 U-Net结构,初步了解要修改的网络,知己知彼,修改起来才能游刃有余。 U-Net存在两个局限,一是网络的最佳深度因应用场景而异,这取决于任务的难度和可用于训练的标注数…...
