mmgrab、mmdrop mmput函数用法

在 Linux 内核开发中,mmgrab()mmdrop()mmput() 是用于管理 struct mm_struct(进程地址空间描述符)引用计数的核心函数。以下是它们的详细用法和区别:


1. mmgrab()

  • 功能增加 mm_struct 的用户引用计数(mm->mm_users)。
  • 使用场景
    • 需要短期访问进程内存空间时(如内核线程处理用户请求)。
    • 确保目标 mm_struct 在操作期间不被释放。
  • 示例
    1
    2
    3
    4
    5
    6
    struct mm_struct *mm = get_task_mm(task);  // 获取目标任务的 mm_struct(已增加引用)
    if (mm) {
    mmgrab(mm); // 显式增加引用计数(可选,因 get_task_mm 已增加)
    // 安全操作 mm(如访问页表)
    mmput(mm); // 操作完成后减少引用
    }
  • 注意
    • mmgrab() 仅增加计数,检查计数是否为零。

2. mmput()

  • 功能减少 mm_struct 的用户引用计数(mm->mm_users)。若计数归零:
    1. 释放内存映射(exit_mmap())。
    2. 减少内核引用计数(mm->mm_count)。
    3. mm_count 归零,彻底销毁 mm_struct
  • 使用场景
    • 配对 mmgrab()get_task_mm(),释放不再需要的引用。
  • 示例
    1
    2
    3
    4
    5
    6
    7
    void process_mm(struct task_struct *task) {
    struct mm_struct *mm = get_task_mm(task); // 获取 mm(增加 mm_users)
    if (!mm) return;

    // 操作 mm...
    mmput(mm); // 释放引用,若 mm_users 归零则触发清理
    }

3. mmdrop()

  • 功能减少 mm_struct 的内核引用计数(mm->mm_count)。
  • 使用场景
    • 仅在内核核心代码中管理 mm_count(如 mmput() 内部或 RCU 回调)。
    • 普通模块/驱动中禁止直接使用
  • mmput() 的区别
    函数 操作的计数器 是否触发资源释放 适用场景
    mmput() mm_users 是(归零时) 用户引用释放
    mmdrop() mm_count 否(需手动处理) 内核底层内存管理
  • 示例(仅在内核核心代码中):
    1
    2
    3
    4
    5
    6
    7
    // mmput() 内部简化逻辑
    void mmput(struct mm_struct *mm) {
    if (atomic_dec_and_test(&mm->mm_users)) { // mm_users 归零
    exit_mmap(mm); // 释放内存映射
    mmdrop(mm); // 减少 mm_count,若归零则释放 mm_struct
    }
    }

关键规则

  1. 配对使用
    mmgrab() / get_task_mm() 必须与 mmput() 成对调用。

    1
    2
    3
    struct mm_struct *mm = get_task_mm(task);  // 或 mmgrab()
    // ... 操作 ...
    mmput(mm); // 确保释放
  2. **禁止直接使用 mmdrop()**:
    除非在核心内存管理代码中(如实现自定义销毁逻辑),否则永远不要调用 mmdrop()

  3. 引用计数类型

    • mm_users:用户引用数(如进程、线程),归零时释放内存映射。
    • mm_count:内核引用数(如 lru 列表),归零时释放 mm_struct 自身。

典型工作流

1
2
3
4
5
6
7
8
9
10
11
12
13
// 1. 获取 mm_struct 引用
struct mm_struct *mm = get_task_mm(target_task);

// 2. 操作地址空间
if (mm) {
down_read(&mm->mmap_lock); // 加锁保护
// 访问 VMA 或页表...
up_read(&mm->mmap_lock);
}

// 3. 释放引用
if (mm)
mmput(mm); // 减少引用,必要时触发销毁

注意:操作 mm_struct 时通常需配合 mmap_lock(读写信号量)确保并发安全。

如有疑问,建议查阅内核源码(mm/memory.ckernel/fork.c)或特定版本文档。