当前位置: 首页 > news >正文

详细介绍Linux 内存管理struct page数据结构中的_count和_mapcount有什么区别?

在Linux内核的struct page中,_count(或_refcount)和_mapcount是两个关键的引用计数成员,它们各自承担不同的职责。以下是深度解析和代码案例:

 

1. _count vs _mapcount 区别详解

 

_count(或_refcount)

 

特性 说明

 

全称 atomic_t _refcount(新内核)或atomic_t _count(旧内核)

核心作用 页面生命周期计数 - 表示内核持有该物理页的"所有者"数量

触发释放条件 当计数值降为0时,页面返回伙伴系统

操作接口 get_page(), put_page(), page_ref_count()

计数值含义 每增加1表示新增一个所有者(如页缓存、匿名页、设备映射等)

数值范围 ≥0

 

作用:记录物理页的引用总数,用于判断页是否可以被释放。

 

递增场景:

 

页被分配给进程(如malloc、fork)。

 

页被内核临时使用(如I/O缓冲)。

 

递减场景:

 

进程释放内存(如free、exit)。

 

内核不再需要该页。

 

临界值:

 

_count == 0:页可以被回收。

 

_count > 0:页正在被使用。

 

 

 

 

_mapcount

 

特性 说明

 

全称 atomic_t _mapcount 或 int _mapcount

核心作用 页表映射计数 - 表示页面被映射到用户空间页表的次数

特殊值 -1: 未被用户进程映射

0: 被1个进程映射

N: 被N+1个进程映射

操作接口 page_mapcount(), page_add_file_rmap(), page_remove_rmap()

计数范围 -1 ~ 未限定上限

 

作用:记录页表映射的数量(即有多少个进程的页表指向该物理页)。

 

递增场景:

 

页被映射到进程的地址空间(如mmap、写时复制)。

 

递减场景:

 

进程取消映射(如munmap、exit)。

 

特殊值:

 

_mapcount == -1:页未被任何进程映射(如仅内核使用)。

_mapcount == 0:页被1个进程映射。

_mapcount > 0:页被多个进程共享(如共享内存)。

 

 

2. 核心差异总结

 

维度 _refcount _mapcount

 

保护对象 物理页面的生命周期 页面在虚拟地址空间的映射关系

计数值=0 页面可被回收 无实际意义(正常值为-1,0或正数)

增加场景 加入页缓存、设备映射等 创建新页表映射(mmap/mprotect)

减少场景 put_page()调用、文件删除等 解除页表映射(munmap)

用户态影响 间接(决定物理页存在) 直接(决定虚拟映射是否有效)

 

3.代码案例:模拟写时复制(COW)

 

以下是一个简化版的内核模块代码,演示_count和_mapcount在写时复制中的变化:

 

#include <linux/module.h>

#include <linux/mm.h>

#include <linux/sched.h>

 

static void print_page_counts(struct page *page) {

    pr_info("_count = %d, _mapcount = %d\n",

            page_ref_count(page),

            page_mapcount(page));

}

 

static int __init cow_demo_init(void) {

    struct page *page;

    struct vm_area_struct *vma;

    unsigned long addr = current->mm->mmap->vm_start;

 

    // 1. 获取用户态地址对应的页

    page = follow_page(current->mm->mmap, addr, FOLL_GET);

    if (!page) {

        pr_err("Failed to get page\n");

        return -EFAULT;

    }

 

    pr_info("Original page:\n");

    print_page_counts(page); // 初始状态:_count=1, _mapcount=0

 

    // 2. 模拟fork():写时复制前(共享映射)

    get_page(page); // _count++

    atomic_inc(&page->_mapcount); // _mapcount++

 

    pr_info("After fork (shared):\n");

    print_page_counts(page); // _count=2, _mapcount=1

 

    // 3. 模拟写入触发COW

    put_page(page); // 原进程释放引用 _count--

    atomic_dec(&page->_mapcount); // 原进程取消映射 _mapcount--

 

    // 新分配COW页

    struct page *new_page = alloc_page(GFP_KERNEL);

    copy_user_highpage(new_page, page, addr, current->mm->mmap);

 

    pr_info("After COW (new page):\n");

    print_page_counts(new_page); // _count=1, _mapcount=0

 

    // 清理

    __free_page(page);

    __free_page(new_page);

    return 0;

}

 

static void __exit cow_demo_exit(void) {

    pr_info("Module exited\n");

}

 

module_init(cow_demo_init);

module_exit(cow_demo_exit);

MODULE_LICENSE("GPL");

 

5. 代码流程解析

 

初始状态:

 

进程A映射一个页:_count=1(A持有),_mapcount=0(未共享)。

fork()后:

 

进程B共享该页:_count=2(A+B持有),_mapcount=1(1次映射)。

写入触发COW:

 

进程B复制新页:原页_count=1(A持有),_mapcount=0(B取消映射)。

新页_count=1(B持有),_mapcount=0(新映射)。

 

6. 关键函数说明

 

page_ref_count(page):获取_count值。

page_mapcount(page):获取_mapcount值。

follow_page():通过虚拟地址获取struct page。

copy_user_highpage():复制页内容(COW核心操作)。

 

7. 实际运行输出示例

 

Original page:

_count = 1, _mapcount = 0

After fork (shared):

_count = 2, _mapcount = 1

After COW (new page):

_count = 1, _mapcount = 0

 

8. 总结

 

_count是物理页的全局引用计数,决定页是否可回收。

_mapcount是映射计数,反映共享状态(如COW、共享内存)。

两者协同工作:即使_mapcount=0(无映射),若_count>0(内核仍在使用),页也不能释放。

 

9.性能优化与注意事项

 

原子操作开销

 

_refcount使用atomic_t确保原子性

高并发场景下可能成为瓶颈,需尽量减少频繁操作

引用循环问题

 

 

// 错误示例:导致永久引用

void set_page_private(struct page *page, void *data)

{

    get_page(page); // 增加额外引用

    page->private = data;

}

多类型页面处理

 

// 处理复合页(Compound Pages)

if (PageCompound(page)) {

    // 整个复合页共享相同_refcount

    // 但每个子页有独立_mapcount

}

页面迁移保护

 

// 迁移前检查

if (page_mapcount(page) > 0 || page_ref_count(page) > 1) {

    return -EBUSY; // 页面被使用中

}

 

10.调试工具

 

CONFIG_DEBUG_VM:检测无效的计数操作

page_owner:追踪页面生命周期所有者

/proc/pagetypeinfo:查看页面计数统计

通过正确理解_refcount和_mapcount的差异及其协同工作方式,开发者可以有效优化内存管理逻辑,避免常见错误如页面泄露或过早释放。

http://www.xdnf.cn/news/1382941.html

相关文章:

  • 图论好题推荐-逛公园
  • 【LInux】常用命令笔记
  • ArkUI框架之Canvas 画布
  • 什么是最小二乘法
  • 二、开关电源的EMC改善措施
  • CVPR2025丨VL2Lite:如何将巨型VLM的“知识”精炼后灌入轻量网络?这项蒸馏技术实现了任务专用的极致压缩
  • 虚幻基础:角色变换角色视角蒙太奇运动
  • 基于SpringBoot的老年人健康数据远程监控管理系统【2026最新】
  • 嵌入式开发学习———Qt软件环境下的C++学习(七)
  • 图论基础篇
  • Mybatis中缓存机制的理解以及优缺点
  • 微服务相关面试题
  • stable-baseline3介绍
  • 个人博客运行3个月记录
  • mac m4执行nvm install 14.19.1报错,安装低版本node报错解决
  • 【STM32】G030单片机的窗口看门狗
  • Flutter:ios打包ipa,证书申请,Xcode打包,完整流程
  • LeetCode Hot 100 第7天
  • mac系统本地部署Dify步骤梳理
  • 仓颉编程语言青少年基础教程:输入输出
  • 模拟实现Linux中的进度条
  • [Mysql数据库] 知识点总结5
  • 天津医科大学肿瘤医院冷热源群控系统调试完成:以 “精准控温 + 高效节能” 守护医疗核心场景
  • 实战演练(一):从零构建一个功能完备的Todo List应用
  • Spring事务管理机制深度解析:从JDBC基础到Spring高级实现
  • 力扣(LeetCode) ——965. 单值二叉树(C语言)
  • C#写的一键自动测灯带的应用 AI帮写的。
  • [灵动微电子 MM32BIN560CN MM32SPIN0280]读懂电机MCU之串口DMA
  • list 手动实现 1
  • 学习日志40 python