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

Linux中处理CPU离线时清理CPU缓存page_alloc_init函数的实现

注册CPU离线通知page_alloc_init

static int page_alloc_cpu_notify(struct notifier_block *self,unsigned long action, void *hcpu)
{int cpu = (unsigned long)hcpu;long *count;if (action == CPU_DEAD) {/* Drain local pagecache count. */count = &per_cpu(nr_pagecache_local, cpu);atomic_add(*count, &nr_pagecache);*count = 0;local_irq_disable();__drain_pages(cpu);local_irq_enable();}return NOTIFY_OK;
}
#endif /* CONFIG_HOTPLUG_CPU */void __init page_alloc_init(void)
{hotcpu_notifier(page_alloc_cpu_notify, 0);
}

1. 函数功能

处理CPU离线事件,清理该CPU的本地页面缓存,确保内存管理的正确性

2. 代码详细解释

2.1. 函数定义和参数解析

static int page_alloc_cpu_notify(struct notifier_block *self,unsigned long action, void *hcpu)
  • static:函数只在当前文件内可见
  • page_alloc_cpu_notify:CPU通知回调函数
  • struct notifier_block *self:通知块结构指针
  • unsigned long action:事件动作类型
  • void *hcpu:包含CPU编号的指针

2.2. 变量声明

        int cpu = (unsigned long)hcpu;long *count;
  • int cpu = (unsigned long)hcpu:将void指针转换为CPU编号
    • hcpu实际上是一个指向CPU编号的指针,需要强制类型转换
  • long *count:指向每CPU页面缓存计数的指针

2.3. CPU离线事件处理

        if (action == CPU_DEAD) {
  • action == CPU_DEAD:检查是否是CPU离线事件
  • CPU_DEAD:表示一个CPU核心已经被关闭(热插拔移除)
  • 只有在CPU离线时才执行后续清理操作

2.4. 合并页面缓存计数

                /* Drain local pagecache count. */count = &per_cpu(nr_pagecache_local, cpu);atomic_add(*count, &nr_pagecache);*count = 0;
  • count = &per_cpu(nr_pagecache_local, cpu)
    • 获取即将离线CPU的本地页面缓存计数指针
    • per_cpu宏获取特定CPU的变量实例
  • atomic_add(*count, &nr_pagecache)
    • 原子操作将本地计数加到全局计数中
    • 确保离线CPU的页面缓存被正确统计
  • *count = 0:将本地计数清零

2.5. 清理CPU的页面

                local_irq_disable();__drain_pages(cpu);local_irq_enable();
  • local_irq_disable():禁用本地CPU的中断
    • 防止在页面清理过程中被中断打断
  • __drain_pages(cpu)核心操作 - 排空指定CPU的页面
    • 释放该CPU持有的所有空闲页面
    • 将这些页面返回到全局内存池中
  • local_irq_enable():重新启用中断

2.6. 函数返回

        }return NOTIFY_OK;
}
  • return NOTIFY_OK:返回通知处理成功
  • NOTIFY_OK:表示通知已被成功处理
  • 即使不是CPU_DEAD事件也返回OK,因为只关心CPU离线事件

2.7. 条件编译结束

#endif /* CONFIG_HOTPLUG_CPU */

详细解释:

  • 结束#ifdef CONFIG_HOTPLUG_CPU的条件编译块
  • 只有在配置了CPU热插拔支持时,这段代码才会被编译

2.8. 初始化函数

void __init page_alloc_init(void)
{hotcpu_notifier(page_alloc_cpu_notify, 0);
}
  • void __init page_alloc_init(void):页面分配器初始化函数
    • __init表示该函数只在系统初始化期间使用
  • hotcpu_notifier(page_alloc_cpu_notify, 0)
    • 注册CPU热插拔通知回调
    • page_alloc_cpu_notify:回调函数指针

排空指定CPU的每CPU页面缓存__drain_pages

#if defined(CONFIG_PM) || defined(CONFIG_HOTPLUG_CPU)
static void __drain_pages(unsigned int cpu)
{struct zone *zone;int i;for_each_zone(zone) {struct per_cpu_pageset *pset;pset = &zone->pageset[cpu];for (i = 0; i < ARRAY_SIZE(pset->pcp); i++) {struct per_cpu_pages *pcp;pcp = &pset->pcp[i];pcp->count -= free_pages_bulk(zone, pcp->count,&pcp->list, 0);}}
}

1. 函数功能

当CPU离线或系统挂起时,清空指定CPU在所有内存区域中的每CPU页面缓存,将这些页面返回到伙伴系统中

2. 代码详细解释

2.1. 条件编译和函数定义

#if defined(CONFIG_PM) || defined(CONFIG_HOTPLUG_CPU)
static void __drain_pages(unsigned int cpu)
  • #if defined(CONFIG_PM) || defined(CONFIG_HOTPLUG_CPU):条件编译
    • 只有在配置了电源管理(CONFIG_PM)或CPU热插拔(CONFIG_HOTPLUG_CPU)时才编译此函数
  • static void __drain_pages(unsigned int cpu):函数定义
    • static:函数只在当前文件内可见
    • __drain_pages:内部函数,通常以__前缀表示
    • unsigned int cpu:要清理的CPU编号

2.2. 变量声明

        struct zone *zone;int i;
  • struct zone *zone:指向内存区域的指针
  • int i:循环计数器,用于遍历每CPU页面的迁移类型

2.3. 遍历所有内存区域

        for_each_zone(zone) {
  • for_each_zone(zone):宏,遍历系统中所有的内存区域(zone)
  • 包括:ZONE_DMA, ZONE_NORMAL, ZONE_HIGHMEM等
  • 对于每个zone,都需要清理指定CPU的页面缓存

2.4. 获取每CPU页面集

                struct per_cpu_pageset *pset;pset = &zone->pageset[cpu];
  • struct per_cpu_pageset *pset:指向每CPU页面集的指针
  • pset = &zone->pageset[cpu]:获取指定CPU在该zone中的页面集
    • zone->pageset:指向每CPU页面集数组的指针
    • [cpu]:索引到特定CPU的页面集

2.5. 遍历所有迁移类型

                for (i = 0; i < ARRAY_SIZE(pset->pcp); i++) {
  • i = 0:从第一个迁移类型开始
  • i < ARRAY_SIZE(pset->pcp):遍历所有迁移类型
  • pset->pcp:每CPU页面数组,包含不同迁移类型的页面列表
  • ARRAY_SIZE:计算数组大小的宏

2.6. 获取每CPU页面结构

                        struct per_cpu_pages *pcp;pcp = &pset->pcp[i];
  • struct per_cpu_pages *pcp:指向每CPU页面结构的指针
  • pcp = &pset->pcp[i]:获取特定迁移类型的每CPU页面结构
    • pset->pcp[i]:第i个迁移类型的每CPU页面缓存

2.7. 释放页面并更新计数

                        pcp->count -= free_pages_bulk(zone, pcp->count,&pcp->list, 0);

参数分解:

free_pages_bulk(zone,          // 目标内存区域pcp->count,    // 要释放的页面数量&pcp->list,    // 页面链表头0)             // 冷热页面标志(0表示不区分)

执行过程:

  1. free_pages_bulk():批量释放页面到伙伴系统

    • pcp->list链表中的所有页面释放回zone的伙伴系统
    • 返回实际成功释放的页面数量
  2. pcp->count -= ...:更新页面计数

    • 从当前计数中减去已释放的页面数量
    • 如果所有页面都成功释放,pcp->count将变为0

很好的问题!通过GDB的输出,我们可以更清楚地理解这些数据结构的关系。让我详细解释:

3. 数据结构关系解析

完整的数据结构层次

struct per_cpu_pageset {struct per_cpu_pages pcp[2];  // 包含2个per_cpu_pages结构
}struct per_cpu_pages {int count;                    // 当前链表中的页面数量int low;                      // 低水位线int high;                     // 高水位线  int batch;                    // 批量操作大小struct list_head list;        // 页面链表头
}

3.1. 详细关系解释

per_cpu_pageset
pcp0: 热页面
pcp1: 冷页面
count
low
high
batch
list
count
low
high
batch
list

3.2. 为什么是pcp[2]? - 冷热页面机制

Linux内核使用冷热页面概念来优化内存分配性能:

// pcp[0] 和 pcp[1] 的区别:
pcp[0]: 热页面 (Hot Pages)   - 很可能在CPU缓存中
pcp[1]: 冷页面 (Cold Pages)   - 不在CPU缓存中

4. 冷热页面的作用和区别

分配时的选择策略

// 当需要分配页面时:
if (分配标志包含 __GFP_COLD) {// 从冷页面链表分配 (pcp[1])// 用于不太可能被立即访问的数据
} else {// 从热页面链表分配 (pcp[0])  // 用于可能被频繁访问的数据
}

实际使用场景

热页面 (pcp[0]):

  • 进程堆栈
  • 进程数据段
  • 内核数据结构
  • 预期会被频繁访问

冷页面 (pcp[1]):

  • DMA缓冲区
  • 文件读写缓存
  • 一次性使用的临时数据
  • 不太可能被CPU频繁访问

5. 水位线控制机制

结构体字段作用

struct per_cpu_pages {int count;      // 当前链表中的页面数量int low;        // 低水位线 - 当count低于此值时补充页面int high;       // 高水位线 - 当count高于此值时返还页面  int batch;      // 每次补充或返还的批量大小struct list_head list;  // 实际的页面链表
}

6. 总结

per_cpu_pagesetper_cpu_pages 的关系是:

  1. 容器与内容pageset 包含2个 pages 结构(冷和热)
  2. 性能优化:通过冷热分离提高CPU缓存利用率
  3. 批量管理:使用水位线机制智能补充和返还页面
  4. 每CPU隔离:每个CPU有自己的缓存,避免锁竞争

__drain_pages中,这个设计意味着:

  • 需要清理两次(冷页面和热页面)
  • 确保CPU离线时所有缓存页面都被释放
  • 维持内存管理的正确性和完整性
http://www.dtcms.com/a/491543.html

相关文章:

  • 单片机开发工具篇:(一)32单片机开发需要的软件和硬件
  • 旅游网站建设网足球比分网站怎么建设
  • 甘肃省建设工程安全质量监督管理局网站官网北京通州做网站
  • 数字隔离器,如何隔绝高、低压回路间的电气隐患?
  • 搜索引擎:Elasticsearch聚合,多维分析怎样实现?
  • 【0441】bgwriter 和 walwriter 都刷脏缓冲区 block到 disk file,两者有何差异?
  • 《算法与数据结构》第七章[算法4]:最短路径
  • 做网站字号多大网络营销推广方案pdf
  • 网站开发需求分析内容淄博网站制作设计高端
  • DOM CDATA
  • 动态规划----过河卒
  • 2025-10-15 ZROJ25普及联考day12 赛后总结
  • 工控人如何做自己的网站网页版微信文件传输助手
  • 南京网站建设网站高端网站建设 案例
  • Qt程序高分辨界面显示不正常解决办法
  • 如何下载Windows 11安装文件
  • Java 大视界 -- 基于 Java 的大数据隐私计算在医疗影像数据共享中的实践探索
  • 版本管理:Git Large File,二进制文件追踪?
  • 网站开发课程报告心得中国十大装修公司品牌排行榜
  • 广告设计培训机构哪家好南京谷歌seo
  • 企业活动网站创意案例铜陵市建设局网站
  • 计算机操作系统——文件系统的全局结构
  • 万万州州微微网站网站建建设设网页设计实训报告主要内容
  • pwn.college之Cryptography模块
  • wordpress 点评类网站找人做网站注意什么
  • 桥接模式详解
  • 【入门级-算法-3、基础算法:二分法】
  • 配置串口与应用
  • python中的浮点数运算
  • 如何解决Redis缓存“数据一致性“问题?