詳細介紹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
英文地址,請注明出處:http://en.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…