Linux内核中如何快速判断物理页是否在被使用

在Linux内核中,判断一个物理页(由 struct page 表示)是否正在被使用,主要通过检查其引用计数(Reference Count)来实现。以下是详细方法和关键点:


核心方法:检查引用计数

  1. 使用 page_count() 函数
    Linux内核提供了辅助函数 page_count() 直接获取页面的引用计数:

    1
    2
    3
    #include <linux/mm.h>

    int active = page_count(page); // 返回引用计数值
    • 返回值含义
      • **> 0**:页面正在被使用(至少有一个引用)。
      • **= 0**:页面未被使用(空闲状态,可能已被释放或可被回收)。
  2. 直接访问 _refcount 成员
    某些场景可能需要更精细的控制,可直接访问 struct page 的引用计数成员(不同内核版本字段名可能不同):

    1
    2
    3
    4
    5
    // 内核版本 >= 4.6
    atomic_t refcount = page->_refcount;

    // 旧版本(如 < 4.6)
    atomic_t refcount = page->_count;

    通过原子操作读取值:

    1
    int count = atomic_read(&page->_refcount);

引用计数的来源

引用计数增加的情况包括:

  • 内核直接引用:通过 get_page()try_get_page() 显式增加计数。
  • 页表映射:用户空间或内核空间映射该页(如进程的页表项指向该页)。
  • I/O 操作:正在进行磁盘I/O(如 struct bio 持有该页)。
  • 缓存关联:页面在页缓存(Page Cache)或匿名页链表中。

注意事项

  1. 并发安全
    引用计数通过原子操作更新,确保多线程环境下的安全性。直接访问时需使用原子函数(如 atomic_read())。

  2. 复合页(Compound Pages)
    大页(如THP)由多个子页组成,主页的引用计数代表整个复合页。使用 page_count() 已自动处理此情况。

  3. 0 计数的特殊含义
    计数为 0 的页面通常处于空闲状态,但需注意:

    • 可能位于伙伴系统(Buddy System)的空闲链表上。
    • 某些短暂状态(如正在释放)也可能短暂出现 0
  4. 避免直接比较计数值
    推荐使用 page_count(page) > 0 而非 == 1,因为页面可能被多次引用(如共享内存)。


示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
// 判断页面是否正在被使用
bool is_page_active(struct page *page) {
return page_count(page) > 0;
}

// 模块中使用示例
void check_page_usage(struct page *page) {
if (page_count(page) > 0) {
printk("Page %px is in use (refcount=%d).\n", page, page_count(page));
} else {
printk("Page %px is free.\n", page);
}
}

其他辅助方法

  • 页标志位检查
    通过 PageXXX() 宏检查页状态(如 PageActive()PageLRU()),但无法直接替代引用计数。
  • 映射状态检查
    使用 page_mapped() 检查是否被页表映射,但仅覆盖部分使用场景(如未映射的页缓存页仍可能被使用)。

总结

**快速判断物理页是否被使用的标准方法是检查 page_count(page) > 0**。该操作高效且线程安全,适用于大多数场景(如内存回收、调试等)。对于复杂需求(如精确追踪引用来源),可结合页标志位或内核工具(如 page_owner 调试功能)。