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

内存管理篇-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.使用示例

#include <linux/percpu.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/mm.h>
#include <linux/mmzone.h>#define NR_PERCPU_PAGES 16struct 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);


http://www.mrgr.cn/news/8030.html

相关文章:

  • OD C卷 - Wonderland游乐园
  • 阵列信号处理2_阵列信号最优处理常用准则(CSDN_20240825)
  • 大话C语言:第46篇 C语言项目工程化之Makefile详解
  • 编程路上的“迷宫逃脱”:从Bug堆到算法之巅的奇妙之旅
  • 3.4-CoroutineScope/CoroutineContext:coroutineScope() 和 supervisorScope()
  • hackit 2018
  • QT上位机学习路线(C++)
  • PHP反序列化二
  • ArcGIS Pro基础:如何将数据和引用地图样式一起打包分享
  • Golang | Leetcode Golang题解之第367题有效的完全平方数
  • rust api接口开发(以登陆和中间件鉴权为例)
  • MidJourney付费失败的原因以及失败后如何取消或续订(文末附MidJourney,GPT-4o教程)
  • 如何在Spring Boot应用中加载和使用TensorFlow模型
  • HTML5 浏览器支持
  • 如何使用ssm实现基于JAVA的网上药品售卖系统
  • java通过JDBC连接mysql和postgres数据库实现读写数据
  • 利用API返回值实现商品信息的自动化更新
  • git提交项目,报403无权限
  • 福特汽车削减电动车计划,聚焦成本控制
  • 支持2.4G频秒变符合GB42590的标准的飞行器【无人机GB42590发射端】