本文共 3263 字,大约阅读时间需要 10 分钟。
SMP 引起的竞态 // 其实是避开了 SMP的竞态 , 因为 每个cpu 一个同名变量,SMP间根本不会有竞态发生不能解决 其他原因 引起的竞态
在 CONFIG_SMP 下才有意义, 不配置 CONFIG_SMP 也能用,但是就失去了他的意义(作用,) // 没有SMP,SMP引起的竞态根本就不存在了 // 而 per-cpu 就是 因为 SMP引起的竞态 才被设计出来的在某些体系结构上,per-CPU变量可使用的地址空间是受限的,要尽量保持这些变量比较小 // 在 arm 上好像没限制per-cpu 并没有利用 同步原语 和 屏障,因为 per-cpu的实现 是 避开了SMP的竞态
https://zhuanlan.zhihu.com/p/157563328https://zhuanlan.zhihu.com/p/260986194
per-cpu 机制 解决了 三个情景下的 per-cpu 变量 1. 内核静态percpu变量 2. 模块静态percpu变量 3. 动态percpu变量 // 每个情景的实现有相同部分,又有不同部分per-cpu 机制 针对 三个情景 提供了 几种初始化,几种api A. per-cpu机制的初始化 setup_per_cpu_areas B1. 内核静态percpu变量的定义 DEFINE_PER_CPU(type, name) B2. 模块静态percpu变量的定义 DEFINE_PER_CPU(type, name) B3. 动态percpu变量的定义 alloc_percpu(type) C. percpu变量地址的获取 this_cpu_ptr(var) D. percpu变量的获取 per_cpu(var,cpu)
给每一个cpu分配了一块内存pcpu_area(xKB)每个cpu的xKB内存都是连续的(同group的才连续).N个cpu对应的内存大小是 N*x一个xKB内存分块 分为 static reserved dynamic// 整个 NR_CPU 个内存块 从 pcpu_base_addr 开始每个cpu对应的内存块 大小是怎么决定的// 根据 下面的三个值 以及一些 最大最小值(某个区间最小不能比什么小) 决定的值为Y,然后Y*cpu个数= x// 1. static_size = __per_cpu_end - __per_cpu_start;// 2. pcpu_embed_first_chunk 的第一个参数:reserved_size// 2. pcpu_embed_first_chunk 的第二个参数:dyn_size从 pcpu_base_addr 开始的 N*x大小的 空间是什么时候申请的//setup_per_cpu_areas->pcpu_embed_first_chunk->alloc_fn/pcpu_dfl_fc_alloc->memblock_virt_alloc_from_nopanic
一个xKB内存分块 分为 static reserved dynamicreserved dynamic 被 chunk 管理 ,而 static 没有用 chunk 管理per-cpu 有一个内存代理商 pcpu_slot , 管理了 这些 reserved dynamicpcpu_slot 也是像 buddy 一样(利用order)管理内存的pcpu_slot 是 从 buddy 和 memblock 申请的内存,申请的内存通过pcpu_chunk_relocate 加入 per-cpu // pcpu_slot 没有内存时,会从buddy申请 其中一个内存块 pcpu_first_chunk 是 从 memblock 申请的 其他的内存 都是从buddy申请的 pcpu_slot 的消费者 per-cpu 创建per-cpu变量的时候,会用到内存,则会从 pcpu_slot 申请 只供 动态percpu变量 使用 alloc_percpu 申请 模块静态percpu变量 也会使用
percpu变量的存放 内核静态percpu变量 被 复制到 每个cpu的内存块的static区域,static 不可扩展(不存在空间不够的情况) 模块静态percpu变量 被 复制到 每个cpu的内存块的reserved区域,空间不够,reserved 可扩展 动态percpu变量 被 放在 每个cpu的内存块的dynamic 区域,空间不够,dynamic 可扩展percpu变量的获取 percpu变量存放后 给出一个 值 ptr,通过this_cpu_ptr(即ptr + delta + pcpu_unit_offsets[cpu]) 获取 percpu变量的 实际地址 针对 内核静态percpu变量 : ptr 的值 位于 __per_cpu_start __per_cpu_end 之间 // 值是在编译的时候决定的 针对 模块静态percpu变量 : ptr 的值 为 chunk->base_addr + off - delta // delta = (unsigned long)pcpu_base_addr - (unsigned long)__per_cpu_start; // 针对cpu0不同的per-cpu变量, off 肯定不同,chunk->base_addr可能不同 针对 动态percpu变量 : ptr 的值 为 chunk->base_addr + off - delta // 和 模块静态percpu变量 的chunk 不是一个chunk // 针对cpu0不同的per-cpu变量, off 肯定不同,chunk->base_addr可能不同
// linux-4.0setup_per_cpu_areas pcpu_embed_first_chunk pcpu_build_alloc_info ptr = memblock_virt_alloc_nopanic(PFN_ALIGN(ai_size), 0) // 1000 areas = memblock_virt_alloc_nopanic(areas_size, 0); // 1000 ptr = alloc_fn(cpu, gi->nr_units * ai->unit_size, atom_size);/pcpu_dfl_fc_alloc memblock_virt_alloc_from_nopanic // 2c000 // ---------------------- per-cpu变量区域的申请,4核心,一个0xb000大小 pcpu_setup_first_chunk group_offsets = memblock_virt_alloc // 4 group_sizes = memblock_virt_alloc // 4 unit_map = memblock_virt_alloc // 10 unit_off = memblock_virt_alloc // 10 pcpu_slot = memblock_virt_alloc( // 78 schunk = memblock_virt_alloc // 44 dchunk = memblock_virt_alloc // 44
转载地址:http://ynigi.baihongyu.com/