ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

page_alloc page_expected_state

page_alloc page_expected_state 检查页的当前状态是否符合“预期状态”。它是页面释放/分配路径上调试检查的一部分用于发现页状态异常“坏页”。一、函数签名static inline bool page_expected_state(struct page *page, unsigned long check_flags)参数参数类型含义pagestruct page *要检查的页check_flagsunsigned long调用方期望页应具有的额外标志位返回值booltrue表示页状态符合预期正常false表示不符合预期异常/坏页。static inline文件内私有 强制/建议内联。在check_new_page()、free_pages_prepare()等调试检查路径上调用。二、背景为什么需要“期望状态”检查1. 页状态异常的危害struct page携带大量状态信息引用计数_refcount映射计数_mapcount标志位flagsPG_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. 引用计数_refcountif (atomic_read(page-_refcount) ! 0) return false;页要被释放/复用引用计数必须归零。不为 0 说明还有人在用它 → 释放是非法的可能 double free 或提前释放。2. 映射计数_mapcountif (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页不应属于 slabPG_buddy页不应已经是空闲 Buddy否则是 double freePG_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释放阶段不应出现的标志位掩码。被释放的页应该是已不再被引用的。
返回列表