详细介绍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.pswp.cn/diannao/97743.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/97743.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

面阵 vs 线阵相机:怎么选不踩坑?选型公式直接套用

面阵vs线阵相机&#xff1a;怎么选不踩坑&#xff1f;选型公式直接套用&#x1f3af;面阵vs线阵相机怎么选不踩坑&#xff1f;&#x1f3af;一、面阵相机&#xff1a;工业检测的“万能选手”&#xff0c;拍全图靠它&#x1f3af;二、线阵相机&#xff1a;大视野/高精度的“专属…

Spring Security 如何使用@PreAuthorize注解

&#x1f9f1; 第一步&#xff1a;环境准备✅ 1. 创建数据库&#xff08;MySQL&#xff09;-- 创建数据库&#xff0c;使用 utf8mb4 字符集支持 emoji 和多语言 CREATE DATABASE security_demo CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;-- 使用该数据库 USE security…

JVM中产生OOM(内存溢出)的8种典型情况及解决方案

Java中的OutOfMemoryError&#xff08;OOM&#xff09;是当JVM内存不足时抛出的错误。本文将全面剖析JVM中产生OOM的各种情况&#xff0c;包括堆内存溢出、方法区溢出、栈溢出等&#xff0c;并提供详细的诊断方法和解决方案。 一、OOM基础概念 1.1 OOM错误类型 Java中的OOM是…

【IEEE出版、EI检索、往届会后3个月检索】第四届信号处理、计算机网络与通信国际学术会议(SPCNC 2025)

第四届信号处理、计算机网络与通信国际学术会议&#xff08;SPCNC 2025&#xff09;将于2025年12月5-7日于中国武汉召开&#xff08;线上同步&#xff09;。为本次会议旨在齐聚海内外信号处理、计算机网络与通信等计算机领域的专家学者&#xff0c;为相关领域研究和从业人员提供…

Spring boot注解介绍

1. Spring 核心注解Spring Boot 是基于 Spring 框架的&#xff0c;所以核心注解依然适用。✅ 常见核心注解Component表示一个通用组件&#xff0c;Spring 会自动扫描并注入到容器中。Component public class MyComponent {public void sayHello() {System.out.println("He…

撤销回退 情况⼆:已经 add ,但没有 commit

撤销回退 情况⼆&#xff1a;已经 add &#xff0c;但没有 commit add 后还是保存到了暂存区呢&#xff1f;怎么撤销呢&#xff1f; 1 # 向ReadMe中新增⼀⾏代码 2 hyb139-159-150-152:~/gitcode$ vim ReadMe 3 hyb139-159-150-152:~/gitcode$ cat ReadMe 4 hello bit 5 hell…

【Linux笔记】命令行与vim基础

一、Linux命令行基础 1. 基本语法命令空格参数&#xff08;可写可不写&#xff09;空格文件&#xff0c;文件夹&#xff08;可写可不写&#xff09;ls列出文件夹中的内容/opt 根目录下的opt文件夹ls-a all显示出所有文件以及隐藏文件/optls-a如果不写则输出一个点&#xff0c;当…

Redis 的整数集合:像分类收纳盒一样的整数专属存储

目录 一、先懂定位&#xff1a;为什么需要整数集合&#xff1f;&#xff08;衔接哈希表&#xff09; 二、整数集合的结构&#xff1a;像 “贴了规格标签的收纳盒” 1. encoding&#xff1a;收纳盒的 “规格标签”&#xff08;核心&#xff1a;决定格子大小&#xff09; 2. …

Linux 进程状态 — 僵尸进程

&#x1f381;个人主页&#xff1a;工藤新一 &#x1f50d;系列专栏&#xff1a;C面向对象&#xff08;类和对象篇&#xff09; &#x1f31f;心中的天空之城&#xff0c;终会照亮我前方的路 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 文章目录进…

React 中 key 的作用

React 中 key 的作用是什么&#xff1f; Date: August 31, 2025 Area: 原理key 概念 在 React 中&#xff0c;key 用于识别哪些元素是变化、添加或删除的。 在列表渲染中&#xff0c;key 尤其重要&#xff0c;因为它能提高渲染性能和确保组件状态的一致性。key 的作用 1&#x…

wpf之附加属性

前言 附加属性是 WPF 中一个非常强大和独特的概念。简单来说&#xff0c;它允许一个对象为另一个在其本身类定义中未定义的属性赋值。 1、定义附加属性 定义一个Watermark的附加属性&#xff0c;该属性的作用是将TextBox的附加属性改变时&#xff0c;TextBox的字体颜色改成灰…

深入浅出 RabbitMQ-消息可靠性投递

大家好&#xff0c;我是工藤学编程 &#x1f989;一个正在努力学习的小博主&#xff0c;期待你的关注实战代码系列最新文章&#x1f609;C实现图书管理系统&#xff08;Qt C GUI界面版&#xff09;SpringBoot实战系列&#x1f437;【SpringBoot实战系列】SpringBoot3.X 整合 Mi…

数字化时代,中小企业如何落地数字化转型

大数据时代&#xff0c;各行各业的行业龙头和大型集团都已经开始了数据管理&#xff0c;让数据成为数据资产。但是在我国&#xff0c;中小企业的数量巨大&#xff0c;很多管理者忽视了这一点&#xff0c;今天我们就来聊一聊中小企业的数字化转型。中小企业需要数字化转型首先要…

Unity笔记(九)——画线功能Linerenderer、范围检测、射线检测

写在前面&#xff1a;写本系列(自用)的目的是回顾已经学过的知识、记录新学习的知识或是记录心得理解&#xff0c;方便自己以后快速复习&#xff0c;减少遗忘。这里只记录代码知识。十一、画线功能Linerenderer画线功能Linerenderer是Unity提供的画线脚本&#xff0c;创建一个空…

刷题记录(8)string类操作使用

一、仅反转字母 917. 仅仅反转字母 - 力扣&#xff08;LeetCode&#xff09; 简单来说输入字符串&#xff0c;要求你返回所有仅字母位置反转后的字符串。 简单看一个样例加深理解&#xff1a; 前后互换&#xff0c;我想思路基本很明显了&#xff0c;双指针&#xff0c;或者说…

用好AI,从提示词工程到上下文工程

前言 随着 AI 大模型的爆发,提示词工程(prompt engineering ) 一度是用户应用 AI ,发挥 AI 能力最重要、也最应该掌握的技术。 但现在,在 “提示词工程”的基础上,一个更宽泛也更强力的演化概念被提出,也就是本文我们要介绍的 “上下文工程(Context Engineering)” …

计算机Python毕业设计推荐:基于Django+Vue用户评论挖掘旅游系统

精彩专栏推荐订阅&#xff1a;在下方主页&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f496;&#x1f525;作者主页&#xff1a;计算机毕设木哥&#x1f525; &#x1f496; 文章目录 一、项目介绍二、…

⸢ 肆 ⸥ ⤳ 默认安全:安全建设方案 ➭ a.信息安全基线

&#x1f44d;点「赞」&#x1f4cc;收「藏」&#x1f440;关「注」&#x1f4ac;评「论」 在金融科技深度融合的背景下&#xff0c;信息安全已从单纯的技术攻防扩展至架构、合规、流程与创新的系统工程。作为一名从业十多年的老兵&#xff0c;将系统阐述数字银行安全体系的建设…

如何用AI视频增强清晰度软件解决画质模糊问题

在视频制作和分享过程中&#xff0c;画质模糊、细节丢失等问题常常影响观看体验。无论是老旧视频的修复还是低分辨率素材的优化&#xff0c;清晰度提升都成为用户关注的重点。借助专业的AI技术&#xff0c;这些问题可以得到有效解决。目前市面上存在多种解决方案&#xff0c;能…

Linux92 shell:倒计时,用户分类

问题 while IFS read -r line;doootweb kk]# tail -6 /etc/passwd user1r4:x:1040:1040::/home/user1r4:/bin/bash useros20:x:1041:1041::/home/useros20:/bin/bash useros21:x:1042:1042::/home/useros21:/bin/bash useros22:x:1043:1043::/home/useros22:/bin/bash useros23…