news 2026/10/6 2:53:34

page_alloc get_pfnblock_bitmap_bitidx

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
page_alloc get_pfnblock_bitmap_bitidx

get_pageblock_bitmap_bitidx()——把两个辅助函数(get_pageblock_bitmap()、pfn_to_bitidx())组合起来,直接算出位图里的具体unsigned long字和该字内的位偏移。


一、函数签名与作用

static __always_inline void get_pfnblock_bitmap_bitidx(const struct page *page, unsigned long pfn, unsigned long **bitmap_word, unsigned long *bitidx)

参数

参数方向含义
pagein目标页(用于取 zone,非 SPARSEMEM 下用于定位位图)
pfnin目标页帧号
bitmap_wordout输出:指向位图中具体那个unsigned long字的指针
bitidxout输出:在该字内的位偏移(0 ~ BITS_PER_LONG-1)

为什么拆成"字指针 + 字内偏移"?

  • 位图是unsigned long[],按字(word)组织。

  • 要操作某一位,需要:

    1. 找到哪个字(unsigned long *);

    2. 找到字内第几位(0 ~ 63/31)。

  • 直接返回unsigned long *+ 位偏移,调用方就能用test_bit/set_bit/clear_bit的底层位操作,或自己做原子操作。

  • 相比前面pfn_to_bitidx()返回"全局位索引",这里进一步分解,便于按字操作(尤其原子操作需要字指针)。

__always_inline

  • 强制内联。这类函数在热路径(如set_pageblock_migratetype、compaction、内存热插拔)频繁调用,内联后零调用开销。


二、编译期断言(BUILD_BUG_ON)

#ifdef CONFIG_MEMORY_ISOLATION BUILD_BUG_ON(NR_PAGEBLOCK_BITS != 8); #else BUILD_BUG_ON(NR_PAGEBLOCK_BITS != 4); #endif BUILD_BUG_ON(__MIGRATE_TYPE_END >= (1 << PB_migratetype_bits));

1.NR_PAGEBLOCK_BITS必须是 8 或 4

#ifdef CONFIG_MEMORY_ISOLATION BUILD_BUG_ON(NR_PAGEBLOCK_BITS != 8); #else BUILD_BUG_ON(NR_PAGEBLOCK_BITS != 4); #endif
  • CONFIG_MEMORY_ISOLATION:内存隔离(用于内存热插拔、CMA 隔离)配置。

  • 启用隔离时:需要更多 pageblock 位(如PB_migrate_skip等),NR_PAGEBLOCK_BITS = 8。

  • 未启用时:NR_PAGEBLOCK_BITS = 4。

  • BUILD_BUG_ON:编译期断言,若常量不等于期望值就编译失败。

  • 目的:保证下面的位运算(如* bitidx / BITS_PER_LONG等)前提成立,防止配置不匹配时悄悄出错。

2. 迁移类型位能容纳所有迁移类型

BUILD_BUG_ON(__MIGRATE_TYPE_END >= (1 << PB_migratetype_bits));
  • PB_migratetype_bits:PB_migrate~PB_migrate_end占用的位数。

  • 1 << PB_migratetype_bits:迁移类型字段能表示的最大值 + 1。

  • __MIGRATE_TYPE_END:迁移类型的总数/结束枚举。

  • 断言:迁移类型数量必须小于字段容量,否则编码不下。

  • 编译期保证,防止有人加迁移类型时忘了扩字段。


三、运行时断言(VM_BUG_ON_PAGE)

VM_BUG_ON_PAGE(!zone_spans_pfn(page_zone(page), pfn), page);
  • VM_BUG_ON_PAGE:调试配置(CONFIG_DEBUG_VM)下才启用的运行时断言。

  • 检查:page所属的 zone必须覆盖 pfn。

  • 若不成立,说明调用方传入了不匹配的 page 与 pfn(例如 pfn 不在该 page 的 zone 内),是严重 bug。

  • 生产内核(未开CONFIG_DEBUG_VM)下该检查被编译掉,零开销。


四、核心计算

bitmap = get_pageblock_bitmap(page, pfn); *bitidx = pfn_to_bitidx(page, pfn); word_bitidx = *bitidx / BITS_PER_LONG; *bitidx &= (BITS_PER_LONG - 1); *bitmap_word = &bitmap[word_bitidx];

步骤分解

1. 取位图基址

bitmap = get_pageblock_bitmap(page, pfn);
  • SPARSEMEM 下取 section 的 usemap,否则取 zone 的pageblock_flags。

  • 得到unsigned long *,指向位图数组开头。

2. 取该 pageblock 的起始位索引

*bitidx = pfn_to_bitidx(page, pfn);
  • 返回该 pageblock 在位图中的起始位索引(已乘NR_PAGEBLOCK_BITS)。

3. 算出该位索引落在哪个字

word_bitidx = *bitidx / BITS_PER_LONG;
  • BITS_PER_LONG:一个unsigned long的位数(64 位系统为 64,32 位为 32)。

  • 除法得到第几个字。

  • 注意:这里用/而不是>>,因为BITS_PER_LONG不一定是编译期常量(虽然通常是),编译器一般会优化成移位。

4. 算出字内位偏移

*bitidx &= (BITS_PER_LONG - 1);
  • BITS_PER_LONG - 1是低位的掩码(如 64-1 = 0x3F)。

  • 按位与等价于*bitidx % BITS_PER_LONG,得到字内偏移(0 ~ BITS_PER_LONG-1)。

  • 复用了*bitidx这个输出参数,先存全局位索引,再改成字内偏移。

5. 输出具体字指针

*bitmap_word = &bitmap[word_bitidx];
  • 基址 + 字索引 = 指向具体那个unsigned long。

结果

调用方拿到:

  • *bitmap_word:指向具体字;

  • *bitidx:字内位偏移。

于是可以:

if (test_bit(*bitidx, *bitmap_word)) // 或 set_bit(*bitidx, *bitmap_word);

或对整字做原子操作(atomic_long_*之类)。


五、为什么要输出"字指针 + 字内偏移"而不是"全局位索引"?

这是关键设计:

1. 便于原子操作

  • 位图操作有时需要原子读改写(如set_bit、clear_bit、test_and_set_bit)。

  • 这些原子操作在底层是对一个unsigned long字做操作(如lock bts)。

  • 如果只知道"全局位索引",每次还要重新算字地址;直接给出字指针更高效。

  • 而且原子操作要求同一个字被一致访问,明确字指针能避免重复计算错误。

2. 性能

  • 调用方可能连续操作同一 pageblock 的多个属性位(如迁移类型的读写)。

  • 一次算出字指针,多次操作,避免重复计算。

3. 语义清晰

  • "字 + 字内偏移"正是位操作 API 的底层模型。


六、完整调用示意

unsigned long *word; unsigned long bit; get_pfnblock_bitmap_bitidx(page, pfn, &word, &bit); /* 读迁移类型字段的某位 */ if (test_bit(bit + PB_migrate, word)) { ... } /* 原子地设置某独立位 */ set_bit(bit + PB_migrate_skip, word);

注意:bit是pageblock 起始位在字内的偏移,具体属性位还要+ pb_bit。
(因为NR_PAGEBLOCK_BITS是 4 或 8,通常不会跨字,但严格来说bit + pb_bit若超过BITS_PER_LONG会落到下一个字——实际实现中 pageblock 起始位一般对齐,不会跨字,BUILD_BUG_ON也间接保证前提。)


七、与前面内容的呼应

前面出现过的这里的关联
get_pageblock_bitmap()这里直接调用它取基址
pfn_to_bitidx()这里直接调用它取起始位索引
NR_PAGEBLOCK_BITS(4/8)CONFIG_MEMORY_ISOLATION决定,编译期断言
PB_migrate/PB_migrate_end/PB_migratetype_bits迁移类型字段容量断言
is_standalone_pb_bit()上层用它判断能否单独操作,底层用本函数定位
VM_BUG_ON_PAGE调试断言,保证 page 与 pfn 匹配

八、小结表

步骤代码结果
编译期断言BUILD_BUG_ON(...)保证NR_PAGEBLOCK_BITS和迁移类型字段容量前提成立
运行时断言VM_BUG_ON_PAGE(...)调试下保证 pfn 在 page 的 zone 内
取基址bitmap = get_pageblock_bitmap(page, pfn)位图数组起始
取全局位索引*bitidx = pfn_to_bitidx(page, pfn)pageblock 起始位
算字索引word_bitidx = *bitidx / BITS_PER_LONG第几个字
算字内偏移*bitidx &= (BITS_PER_LONG - 1)字内第几位
输出字指针*bitmap_word = &bitmap[word_bitidx]具体字地址

一句话总结

get_pfnblock_bitmap_bitidx()把"定位 pageblock 位图基址"和"算 pageblock 起始位索引"两步合并,再进一步分解为具体unsigned long字指针 + 字内位偏移,并用BUILD_BUG_ON/VM_BUG_ON_PAGE在编译期和调试期守住前提;输出"字 + 偏移"的形式便于调用方直接做位操作和原子操作,是 pageblock 属性读写的最终定位函数。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/6 2:53:15

Python ai-care 包详解与实战案例

1. 引言ai-care 是一个面向 Python 开发者的 AI 辅助编程工具包&#xff0c;旨在帮助开发者更高效地编写、审查和维护代码。它提供了一系列实用的功能模块&#xff0c;涵盖代码生成、代码审查、文档生成、测试辅助等多个方面&#xff0c;能够显著提升日常开发效率。本文将从功能…

作者头像 李华
网站建设 2026/10/6 2:52:48

27_实验二十六_etc与dev目录构建

实验二十六 构建 etc 与 dev 目录——给骨架装上"灵魂"对应课件&#xff1a;《第6章 构建Linux根文件系统-V2》6.2 节&#xff08;中&#xff09;&#xff0c;Slide 34-48 系列说明&#xff1a;本系列基于华清远见 FS-MP1A&#xff08;STM32MP157A&#xff09;开发板…

作者头像 李华
网站建设 2026/10/6 2:52:38

ABAP CDS 访问控制,把数据读取权限落实到每一条业务记录

同一张销售订单报表,总部财务需要查看多个公司的订单,海外子公司的财务只应查看本公司的订单。页面可以完全一样,查询程序也可以完全一样,真正需要变化的是返回的数据范围。我们在设计这类应用时,不能只考虑菜单是否可见、按钮是否可用,还要落实到一个更具体的问题,当前…

作者头像 李华
网站建设 2026/10/6 2:52:32

信创适配|织灵 Coda Loom 2.0 通过统信软件服务器操作系统双版本互认证

2026 年 9 月&#xff0c;可达智灵与统信软件技术有限公司完成产品互认证&#xff1a;织灵 Coda Loom 系统 V2.0 在统信服务器操作系统 V20、V25 两个版本上整体运行稳定&#xff0c;满足功能及兼容性测试要求。两份《统信软件产品互认证证明》认证日期均为 2026 年 9 月 3 日&…

作者头像 李华
网站建设 2026/10/6 2:48:35

YOLO26小目标AP上不去?三个核心死穴精准打击,工业场景实测涨3.7%

最近在做PCB板缺陷检测的项目&#xff0c;拿YOLO26-s训了一版&#xff0c;大目标的焊盘、插件检测AP都能到95%以上&#xff0c;偏偏针脚虚焊、微孔划痕这类2020以下的小目标&#xff0c;AP卡在27%死活上不去。 调过anchor比例&#xff0c;加过CBAM注意力&#xff0c;把FPN层砍了…

作者头像 李华