当前位置: 首页 > 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.dtcms.com/a/355387.html

相关文章:

  • Pyomo、PuLP 和 OR-Tools 解决约束优化问题效率对比
  • C# SIMD编程实践:工业数据处理性能优化案例
  • 基于SpringBoot的校园资料分享系统【2026最新】
  • 数据结构-哈夫曼树和B树
  • 安宝特方案丨安宝特工业AR全链路解决方案
  • Centos 8 磁盘扩展xfs文件系统 (LVM)
  • 利用 Java 爬虫获取 AQI 详情数据(代码示例)实战指南
  • 如何使用Windows自带的PnPUtil命令来禁用/停用和启用硬件设备
  • VPC私有域名解析DNS
  • 使用 Action 自动部署 VuePress 到 GitHub Pages
  • GRE隧道IPv6过渡技术
  • 数制与编码
  • 并发编程——04 深入理解CASAtomic原子操作类详解
  • Qt 中日志级别
  • JS中的String总结
  • Linux 环境源码安装 Docker
  • 影石insta360 DevOps工程师一面记录
  • 学习嵌入式之驱动——I2C子系统
  • 搭建一个Spring cloud 非阻塞式微服务架构
  • 任天堂NDS中文游戏ROM精选毕业整合包整理分享! +NDS模拟器
  • 使用Docker搭建StackEdit在线MarkDown编辑器
  • 如何通过docker进行本地部署?
  • 企业内部机密视频安全保护|如何防止企业内部机密视频泄露?
  • (附源码)基于Spring Boot公务员考试信息管理系统设计与实现
  • GitLab 配置 Pipeline 的过程
  • linux 网络:协议及Wireshark抓包工具的使用
  • Elasticsearch冷热架构:性能与成本完美平衡
  • 《深入浅出 Node.js》分享精简大纲
  • linu 网络 :TCP粘包及UDP
  • 软件设计师备考-(五)计算机网络