内存管理篇-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 list[3], 指向了三种不同页表链表(可移动的,不可移动的,可回收的),并且三个链表都是的块大小都是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);
相关推荐
大猫子的技术日记1 天前
[百题重刷]前缀和 + Hash 表:缓存思想, 消除重复计算
java·缓存·哈希算法
愤怒的山羊1 天前
jetcache List 缓存, json 序列化 泛型解析成了 JsonObject 处理
缓存·json·list
树在风中摇曳1 天前
带哨兵位的双向循环链表详解(含 C 代码)+ LeetCode138 深度解析 + 顺序表 vs 链表缓存机制对比(图解 CPU 层级)
c语言·链表·缓存
斯文~1 天前
「玩透ESA」站点配置阿里云ESA全站加速+自定义规则缓存
阿里云·缓存·云计算·cdn·esa
S***t7141 天前
Python装饰器实现缓存
缓存
天硕国产存储技术站1 天前
3000次零失误验证,天硕工业级SSD筑牢国产SSD安全存储方案
缓存·固态硬盘·国产ssd
前端炒粉1 天前
35.LRU 缓存
开发语言·javascript·数据结构·算法·缓存·js
努力发光的程序员2 天前
互联网大厂Java面试:从Spring Boot到微服务架构
spring boot·缓存·微服务·消息队列·rabbitmq·spring security·安全框架
zero13_小葵司2 天前
JavaScript性能优化系列(八)弱网环境体验优化 - 8.3 数据预加载与缓存:提前缓存关键数据
javascript·缓存·性能优化
CS_浮鱼2 天前
【Linux进阶】mmap实战:文件映射、进程通信与LRU缓存
linux·运维·c++·缓存