per-CPU(未完待续)

本文介绍了Linux内核中的per-CPU变量机制,探讨了该机制如何为每个CPU分配独立的内存空间,避免同步开销,并详细解释了静态、预留及动态内存区域的概念及其用途。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简介

  • per-CPU变量是内核的一个重要机制,正如名称所示,per-CPU变量为每个cpu单独提供内存空间,每个cpu只访问修改各自的空间
  • 一个per-CPU变量所需要的内存大小为:变量类型大小乘以cpu数量,即sizeof(type) x (number of cpus)
    • 在NUMA系统中,计算所占内存大小的公式与上面类似,但是实际上cpu数量的计算更复杂
  • 事实上,不管哪一种同步API都会带来或多或少的性能开销,但是一个per-CPU数据包含每个cpu自己的唯一私有数据,一个cpu不应该访问其他cpu对应的数据,所以按照这样逻辑不再需要使用同步API。注意:“只有这个cpu能访问这个数据“就是一种编程约定,我们需要确保当前cpu只会访问它自己的唯一数据。
  • 很多内核子系统使用了这个机制:
    • 内存管理的高速缓存机制
      • Page分配器
      • SLUB/SLAB分配器
    • 经常需要更新的性能统计计数器,需要高效同步机制的例子
      • vm统计
      • 网络统计
    • 基础设施RCU
    • 性能剖析profiling、Ftrace
    • VFS虚拟文件系统

下图是一个简单的per-CPU计数器例子
这里写图片描述

特点

Unit

  • 每个cpu数据将放入每个cpu对应的unit内存空间
  • unit不仅可能包含三种不同类型的内存区域,也有可能只包含一种内存区域
    • Static内存区域
      • 使用DEFINE_PER_CPU()宏声明per-CPU变量,其被编译进kernel映像中,在系统启动时为每个cpu定义这个变量
      • 默认大小0x3ec0
    • Reserved内存区域
      • 使用DEDINE_PER_CPU()宏声明per-CPU变量,其被编译进模块文件中,在加载模块时为每个cpu定义这个变量
      • 默认大小0x2000(8KiB)
    • Dynamic 内存区域
      • 借助alloc_percpu函数动态地分配每个cpu变量所占的内存空间
      • 默认大小0x5000(20KiB)
        • 在64位系统上,默认大小28KiB
  • unit大小等于
    • pcpu_unit_pages
      • 组成一个Unit的页帧数
      • 通常大小为 16
    • pcpu_unit_szie
      • 组成一个Unit的字节大小(单位Byte)
      • 通常大小为 0x10000(64KiB = 16 * 4KiB (PAGE_SIZE))

这里写图片描述

这里写图片描述

CPU -> Unit 映射

下图是一个在NUMA系统中cpu与unit映射的例子,(这个NUMA系统最大支持32CPU,实际有12CPU可用)
这里写图片描述

Chunk

per-CPU机制初始化

<think>嗯,用户想了解per-cpu_rwsem的实现细节和在内核编程中的使用。首先,我需要回忆一下per-cpu_rwsem的相关知识。per-cpu_rwsem是Linux内核中的一种读写信号量,它通过每CPU计数器来优化传统读写信号量的扩展性问题。传统读写信号量在高竞争场景下性能可能不佳,而per-cpu_rwsem通过减少锁争用来提升性能。接下来,我需要考虑它的实现细节。比如,每CPU计数器是如何工作的,写者如何获取锁,读者如何进入和退出。可能涉及到的结构体有structrw_semaphore,以及相关的函数如percpu_down_read、percpu_up_read等。写者可能需要使用percpu_down_write来获取独占访问权,而读者则使用percpu_down_read来进入临界区。然后,用户可能关心的使用场景,比如RCU机制、内存管理子系统等。需要举一些具体的例子,比如在mm/rmap.c中的使用情况。此外,还需要提到优化方法,比如避免长时间持有写锁,以减少对读者性能的影响。还要注意用户提到的引用部分,虽然之前的引用是关于mutex和上下文切换的开销,但per-cpu_rwsem的设计正是为了减少这种开销,所以可以适当关联。不过用户指出引用仅供参考,所以重点还是放在per-cpu_rwsem本身。可能生成的相关问题包括per-cpu_rwsem与传统读写信号量的对比,如何在内核模块中使用它,性能分析,以及常见问题排查。这些问题能帮助用户更全面地理解该机制。</think>### per-cpu_rwsem 实现细节 #### 核心设计 per-cpu_rwsem 是 Linux 内核中针对读写信号量(rw_semaphore)的扩展性优化实现,主要特点: 1. **每CPU计数器**:通过维护每个CPU的本地计数器,减少全局锁争用 2. **写者优先**:使用全局原子计数器处理写者请求,保证写者优先 3. **快速路径**:读者通过`this_cpu_inc()`原子操作快速进入临界区[^1] #### 关键数据结构 ```c // 内核实现片段(简化版) struct rw_semaphore { atomic_t count; struct percpu_counter readers; struct task_struct *writer; }; ``` #### 工作流程 1. **读者进入**: ```c void percpu_down_read(struct rw_semaphore *sem) { preempt_disable(); // 禁用抢占 this_cpu_inc(sem->readers); // 本地计数器递增 smp_mb(); // 内存屏障 } ``` 2. **写者获取**: ```c void percpu_down_write(struct rw_semaphore *sem) { atomic_inc(&sem->count); // 全局计数器递增 synchronize_rcu(); // 等待现有读者退出 } ``` #### 性能优化 - **批量更新**:通过`percpu_counter_sum()`合并多个CPU的计数器更新 - **RCU同步**:利用RCU机制实现无锁读者检测 - **自适应切换**:当检测到写者等待时,自动切换到传统读写信号量模式 ### 典型使用场景 1. **RCU机制**:用于保护RCU宽限期(grace period)[^1] 2. **内存管理**:在mm/rmap.c中保护反向映射数据结构 3. **文件系统**:XFS等文件系统的元数据保护 ### 开发注意事项 1. **读者上下文**:必须保证读者在临界区内不睡眠 2. **写者延迟**:写者需要等待所有现有读者退出 3. **调试支持**:可通过`CONFIG_DEBUG_RWSEMS`开启调试检查
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值