内存管理篇-06Per-CPU页帧缓存

per-CPU缓存是对伙伴系统的完善,也是伙伴系统中的一部分。再回顾一下zone结构体的内容,这里的__percpu *pageset实际上就是Per-CPU的实现机制,所以这里的内存实际上最少有三部分,(1)free_area管理了大部分的公共伙伴系统内存;(2)lowmem_reserve预留了一部分;(3)然后就是__percpu *pagset这里对每个CPU都分配一部分管理起来:

1.per-CPU缓存的定义

是一种缓存机制,对伙伴系统的完善。由于伙伴系统管理的页面都是全局的,每个进程在申请页面的时候都需要加锁解锁等操作,极大的引入了开销。为了提高效率就引入页帧缓存,为每个cpu提供一个变量指针__percpu *pageset(定义在struct zone),这样每个cpu就不用去加锁解锁申请,直接使用本地物理页面。

把单个物理页面的申请和释放做成缓存,每个cpu都有这个链表。给每个cpu本地定义一个页表,维护这样一个变量。因此,不需要去全局伙伴系统上去申请释放。

2.核心结构体和实现

可以理解为struct zone 把内存分为了几大块,__percpu *pageset指针(每个CPU都有这么一个指针),指向了一片内存区域。这片区域不需要再各个CPU之间进行同步,struct list_head list3, 指向了三种不同页表链表(可移动的,不可移动的,可回收的),并且三个链表都是的块大小都是4KB。

3.每个CPU分配缓存如何实现

struct per_cpu_pageset __percpu *pageset;这里就是宏,在每个CPU定义这样一个变量,并且每个CPU自己申请和释放内存的时候,不会去从伙伴系统申请释放内存,而是从自己的缓存中。。

在 Linux 内核中,__percpu 是一个特殊的类型修饰符,用于表示一个变量在每个 CPU 上都有一个独立的副本。struct per_cpu_pageset 是一个用于在每个 CPU 上维护一个页面集合的数据结构。这种机制允许每个 CPU 在本地访问页面集合,从而减少跨 CPU 的同步开销。在内核启动时,为每个 CPU 分配一个 struct per_cpu_pageset 的实例,并将其指针存储在一个全局数组中。通过调用 get_cpu_var() 函数来访问当前 CPU 的 struct per_cpu_pageset 实例。访问完之后,通常需要调用 put_cpu_var() 函数来释放对 struct per_cpu_pageset 的引用。

4.使用示例

cpp 复制代码
#include <linux/percpu.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/mm.h>
#include <linux/mmzone.h>

#define NR_PERCPU_PAGES 16

struct per_cpu_pageset {
    spinlock_t lock;
    struct page *pages[NR_PERCPU_PAGES];
    unsigned long nr_pages;
};

static DEFINE_PER_CPU(struct per_cpu_pageset *, pageset);

static void init_per_cpu_pageset(void)
{
    int cpu;

    for_each_possible_cpu(cpu) {
        struct per_cpu_pageset *ps = alloc_percpu(struct per_cpu_pageset);
        if (!ps) {
            printk(KERN_ERR "Failed to allocate per-cpu pageset\n");
            continue;
        }

        ps->nr_pages = 0;
        spin_lock_init(&ps->lock);
        per_cpu(pageset, cpu) = ps;
    }
}

static void release_per_cpu_pageset(void)
{
    int cpu;

    for_each_possible_cpu(cpu) {
        free_percpu(per_cpu(pageset, cpu));
    }
}

static void add_page_to_set(struct page *page)
{
    int cpu = raw_smp_processor_id();
    struct per_cpu_pageset *ps = per_cpu(pageset, cpu);

    spin_lock(&ps->lock);

    // 检查是否还有空间添加页面
    if (ps->nr_pages < NR_PERCPU_PAGES) {
        ps->pages[ps->nr_pages++] = page;
    } else {
        printk(KERN_WARNING "Per-CPU pageset full on CPU %d\n", cpu);
    }

    spin_unlock(&ps->lock);
}

static void remove_page_from_set(struct page *page)
{
    int cpu = raw_smp_processor_id();
    struct per_cpu_pageset *ps = per_cpu(pageset, cpu);
    int i;

    spin_lock(&ps->lock);

    // 查找页面并移除
    for (i = 0; i < ps->nr_pages; i++) {
        if (ps->pages[i] == page) {
            ps->pages[i] = ps->pages[--ps->nr_pages];
            break;
        }
    }

    spin_unlock(&ps->lock);
}

static int __init init_module(void)
{
    init_per_cpu_pageset();
    return 0;
}

static void __exit cleanup_module(void)
{
    release_per_cpu_pageset();
}

module_init(init_module);
module_exit(cleanup_module);
相关推荐
ofoxcoding6 天前
在AI API聚合平台配置DeepSeek V3.2提示词缓存实战:快速接入与成本优化指南
人工智能·spring·缓存·ai
NeilYuen6 天前
gRPC结合FAISS构建AI助手语义缓存模块(一):设计
人工智能·缓存·faiss
taocarts_bidfans6 天前
反向海淘跨境缓存架构优化:taocarts Redis分层缓存实战技术
redis·缓存·架构·反向海淘·taocarts
退休倒计时6 天前
【每日一题】LeetCode 146. LRU 缓存 TypeScript
算法·leetcode·缓存·typescript
炘爚6 天前
Linux——Redis
数据库·redis·缓存
小挪号底迪滴6 天前
Redis 和 MySQL 数据不一致怎么办?缓存更新策略实战
redis·mysql·缓存
闪电悠米6 天前
黑马点评-Redis ZSet-实现关注 Feed 流
服务器·网络·数据库·redis·缓存·junit·lua
Saniffer_SH7 天前
【高清视频】Gen6 服务器还没到,Gen6 SSD 怎么测?Emily 现场演示三种测试环境
人工智能·驱动开发·测试工具·缓存·fpga开发·计算机外设·压力测试
AC赳赳老秦7 天前
OpenClaw + 飞书多维表格:自动同步数据、生成统计图表、触发自动化任务
java·大数据·python·缓存·自动化·deepseek·openclaw