页结构损坏/异常时的报错函数。它是内核内存管理调试机制的"最后一道防线",在发现struct page状态异常时打印诊断信息并采取保护措施。
一、函数签名
static void bad_page(struct page *page, const char *reason)
参数
| 参数 | 类型 | 含义 |
|---|---|---|
page | struct page * | 出问题的页 |
reason | const char * | 出问题的原因描述(字符串字面量) |
返回值
void:无返回值。
static
文件内私有,只在
page_alloc.c内部使用。由
check_new_page()、free_pages_prepare()、bad_range()等调试检查调用。
二、作用
当内核在分配/释放/操作页时发现struct page的状态不符合预期,说明:
页结构被损坏;
引用计数错误;
页的 flag 状态异常;
页被重复释放/释放未分配页;
页属于错误的 zone;
等等。
此时bad_page()负责:
打印详细诊断信息(页地址、pfn、flags、引用计数、mapcount、原因等);
采取保护措施,避免损坏扩散。
三、典型实现
static void bad_page(struct page *page, const char *reason) { static unsigned long resume; static unsigned long nr_shown; static unsigned long nr_unshown; /* * 允许通过内核参数控制打印频率,避免刷屏 * (bad_page 可能被反复触发) */ unsigned long nr = ...; /* 如果不是第一次,可能只打印摘要或抑制 */ if (nr_shown == 0) pr_alert("BUG: Bad page state in process %s pfn:%05lx\n", current->comm, page_to_pfn(page)); /* 打印 page 详情 */ dump_page(page, reason); /* 打印调用栈,定位谁触发的 */ dump_stack(); /* 统计并可能抑制后续打印 */ ... /* * 重要:把 page 标记为"坏的",避免再次被使用 * 通常清除相关标志、设置 PageReserved 或类似, * 使该页不再参与分配。 */ ... }(不同内核版本实现差异较大,早期版本有bad_page计数器nr_shown/nr_unshown抑制刷屏;现代内核更倾向于用dump_page()+dump_stack()+pr_alert。)
核心动作
1. 打印头部信息
pr_alert("BUG: Bad page state in process %s pfn:%05lx\n", current->comm, page_to_pfn(page));pr_alert:最高日志级别,确保输出。current->comm:当前进程名,帮助定位哪个进程触发的。page_to_pfn(page):出问题的 pfn。
2. 打印页详情
dump_page(page, reason);
dump_page()(定义在mm/debug.c):打印 page 的 flags、refcount、mapcount、mapping、index 等;
reason作为附加说明;可能还会打印所属 zone、是否 slab、是否 compound 等。
3. 打印调用栈
dump_stack();
打印当前调用栈,定位是哪个代码路径触发的 bad_page。
这是调试的关键信息。
4. 抑制刷屏
早期实现用静态变量
resume、nr_shown、nr_unshown控制:第一次打印详细信息;
后续可能只打印简要信息或按频率抑制;
避免大量坏页导致日志爆炸、系统卡死。
现代实现可能用
printk_ratelimit()或类似机制。
5. 保护措施
把 page 标记为"不再使用",防止损坏扩散。常见做法:
清除
PageBuddy、PageSlab等标志;设置引用计数为某个安全值;
有时设置
PageReserved,让伙伴系统忽略它;或者在严重情况下触发
BUG()/ panic(取决于配置)。
四、为什么叫 "bad_page"?
内核把状态异常的
struct page称为 "bad page"。可能是:
双重释放(double free):页已释放又被释放;
释放未分配页:页从未分配就被释放;
引用计数错误:
_refcount异常;zone 归属错误:页不属于它声称的 zone;
flags 异常:如既有
PageBuddy又有PageSlab(互斥状态同时出现);内存损坏:物理内存位翻转、野指针写坏了 page 结构。