☰
page_alloc page_expected_state
2026/10/8 2:15:44 网站建设 项目流程

检查页的当前状态是否符合“预期状态”。它是页面释放/分配路径上调试检查的一部分,用于发现页状态异常(“坏页”)。


一、函数签名

static inline bool page_expected_state(struct page *page, unsigned long check_flags)

参数

参数类型含义
pagestruct page *要检查的页
check_flagsunsigned long调用方期望页应具有的额外标志位

返回值

  • bool:true表示页状态符合预期(正常);false表示不符合预期(异常/坏页)。

static inline

  • 文件内私有 + 强制/建议内联。

  • 在check_new_page()、free_pages_prepare()等调试检查路径上调用。


二、背景:为什么需要“期望状态”检查?

1. 页状态异常的危害

struct page携带大量状态信息:

  • 引用计数_refcount;

  • 映射计数_mapcount;

  • 标志位flags:PG_locked、PG_dirty、PG_slab、PG_buddy、PG_hwpoison等;

  • 私有字段private。

如果这些状态不符合预期,说明:

  • 页被重复释放(double free);

  • 页被释放时仍被引用(refcount 不为 0);

  • 页的标志位异常(如同时PG_slab和PG_buddy);

  • 页被内存损坏(位翻转、野指针写入);

  • 页是硬件毒页(PG_hwpoison)。

这些错误若不及时发现,会扩散成难以调试的崩溃或数据损坏。

2. 调试配置下的主动检查

在CONFIG_DEBUG_VM等配置下,内核会在分配新页和释放页时做一系列一致性检查:

  • check_new_page():分配新页后检查页是否处于“干净可用的初始状态”;

  • free_pages_prepare():释放页前检查页是否处于“可安全释放的状态”。

page_expected_state()就是这些检查中的核心判断函数。


三、典型实现

static inline bool page_expected_state(struct page *page, unsigned long check_flags) { /* * 1. 引用计数必须为 0(页没有被使用) */ if (atomic_read(&page->_refcount) != 0) return false; /* * 2. 映射计数必须为 -1 或 0(页没有被映射) * (不同版本约定不同,有的用 page_mapcount()) */ if (page_mapcount(page) != -1 && page_mapcount(page) != 0) return false; /* * 3. 检查标志位: * - 不能有 PG_slab / PG_buddy / PG_hwpoison 等互斥标志; * - 不能有 check_flags 中未允许的位。 */ if (unlikely(page->flags & check_flags)) return false; /* * 4. 某些版本还检查 page->mapping、page->private 等 */ ... return true; }

核心检查项

1. 引用计数_refcount

if (atomic_read(&page->_refcount) != 0) return false;
  • 页要被释放/复用,引用计数必须归零。

  • 不为 0 说明还有人在用它 → 释放是非法的(可能 double free 或提前释放)。

2. 映射计数_mapcount

if (page_mapcount(page) != -1 && page_mapcount(page) != 0) return false;
  • 页不应被任何页表映射。

  • -1或0表示无映射(具体约定随版本)。

3. 标志位检查

if (unlikely(page->flags & check_flags)) return false;
  • check_flags是调用方给出的“不应出现的标志位掩码”。

  • 如果页带有这些位之一 → 状态异常。

  • 典型check_flags包括:

    • PG_slab:页不应属于 slab;

    • PG_buddy:页不应已经是空闲 Buddy(否则是 double free);

    • PG_hwpoison:页不应是硬件毒页;

    • PG_owner_priv_1等;

    • 具体组合由调用方决定。

4. 其他字段

  • 某些版本还检查page->mapping是否为空、page->private是否异常等。

  • 视内核版本和配置而定。


四、check_flags参数的意义

check_flags让不同调用场景可以指定各自关心的标志位:

分配新页时(check_new_page())

if (!page_expected_state(page, PAGE_FLAGS_CHECK_AT_PREP)) { bad_page(page, "unexpected page state at alloc"); return false; }
  • PAGE_FLAGS_CHECK_AT_PREP:分配准备阶段不应出现的标志位掩码。

  • 分配到的页应该是干净、未被使用的。

释放页时(free_pages_prepare())

if (!page_expected_state(page, PAGE_FLAGS_CHECK_AT_FREE)) { bad_page(page, "unexpected page state at free"); ... }
  • PAGE_FLAGS_CHECK_AT_FREE:释放阶段不应出现的标志位掩码。

  • 被释放的页应该是已不再被引用的。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询