背景
在定位linux crash问题过程中,在使用rd读取堆栈(比如rd -s 0xffffabcd 64)这种的时候经常会看到.data…percpu的字段,是什么?怎么定义的?有什么问题?如何获取实际地址?
基础信息
在 Linux 内核中,.data…percpu 是一个特殊的段,用于存放 per-cpu 变量。这些变量是为每个 CPU 分配的独立副本,以避免多核处理器在访问共享数据时产生的竞争。这种机制允许每个 CPU 访问自己的数据副本,从而提高了访问效率。
作用:
- 避免竞争: 多核不用加锁
- 提高性能: 利用本地缓存提高性能
要点:
-
获取实际地址的方式,从原来的
rd -s 0xffffabcd 64这里的-s参数修改为-x 然后找到对应堆栈的地址。 -
所在代码段:
.data..percpu -
在内核启动时,需要为每个 CPU 分配并初始化 per-cpu 变量的内存区域。会将 .data…percpu 段的内容复制到每个 CPU 的内存区域。
-
在某些体系结构上,per-cpu 变量可使用的地址空间是受限的,需要尽量保持变量较小
-
需要使用特定的函数进行访问和操作
-
从性能上看,可以避免或者减少使用锁的,还可以CPU 访问的是自己的数据,可以充分利用 缓存(如 L1 Cache),从而提高访问速度。
减少缓存行冲突: 在多处理器系统中,多个 CPU 访问同一缓存行会导致缓存行冲突,影响性能。percpu 变量的实现会尽量将每个 CPU 的副本放在不同的缓存行中,从而减少这种冲突。 -
定义方式
#define DEFINE_PER_CPU(type, name) \
DEFINE_PER_CPU_SECTION(type, name, "")
#define DEFINE_PER_CPU_SECTION(type, name, sec) \
__PCPU_ATTRS(sec) __typeof__(type) name
#endif
#define __PCPU_ATTRS(sec) \
__percpu __attribute__((section(PER_CPU_BASE_SECTION sec))) \
PER_CPU_ATTRIBUTES
#define PER_CPU_BASE_SECTION ".data..percpu"
可以看到是定义变量的使用了__attribute__的编译关键字,放入了.data..percpu的代码段
declare的原理
可以看到就是多了个extern
#define DECLARE_PER_CPU(type, name) \
DECLARE_PER_CPU_SECTION(type, name, "")
#define DECLARE_PER_CPU_SECTION(type, name, sec) \
extern __PCPU_ATTRS(sec) __typeof__(type) name
用法举例
## 定义
DEFINE_PER_CPU(long, nvfs_n_ops); //可以看到这个定义使用long类型的并且放到了.data..percpu代码段
## 申明
DECLARE_PER_CPU(long, nvfs_n_ops);
## 获取当前CPU的value
this_cpu_inc(nvfs_n_ops);
this_cpu_dec(nvfs_n_ops);
## 统计所有cpu的值
int i;
long sum = 0;
for_each_possible_cpu(i) //遍历cpu
sum += per_cpu(nvfs_n_ops, i); //这里根据per_cpu配合第二个参数获取i
return sum;
其他
- 再来看内核ld链接脚本放在哪儿的?
可以看到linux内核链接脚本的SECTIONS中这个PERCPU_SECTION离BSS段不远。

- 遇到过的坑:percpu的地址如果是其他cpu访问可能会有一些问题,不如可能出现page request 失败。
- 适用的场景:1.频繁更新的数据比如网络子系统中的计数器,使用 percpu 变量可以避免每次更新时的锁操作,提高效率。2.CPU 特定的数据: 比如 CPU 的 ID、当前运行的线程等,使用 percpu 变量可以方便地存储和访问这些数据
综述
percpu的发明主要是为了解决在多处理器系统中共享数据访问时的同步问题,提高系统的性能和效率。实现机制是在内存中专门分配特定的区域,然后加载的时候给不同的CPU使用。
654

被折叠的 条评论
为什么被折叠?



