环境
linux 4.19
关于perCPU变量
percpu变量的介绍,这位老哥做了介绍,包括为什么要有这样的变量以及优势:linux内核之Per-CPU变量,我把这个老哥的总结复制下来:
- 通过Per-cpu变量除了可以分配内存,还有一个最大的好处就是不需要考虑同步。最好的同步技术就是把不需要同步的内核放在首位,因为每种显示的同步原语都有不容忽视的开销。
- 本质:Per-cpu变量主要是数据结构的数组,每个cpu对应数组中的一个元素。
- 位置:Per-cpu数组在主存中被排列,以使每个数据结构放在硬件高速缓存中的不同行(参考第二章硬件高速缓存)。这样,对每cpu数组的并发访问不会造成高速缓存行的窃用和实效(这种操作会带来昂贵的系统性能开销)。
同时简单讲了下percpu变量的初始化方法和获取方式。但是这篇老哥没有讲的很细。但是这篇博客讲的很细节:PERCPU变量实现
最终,获取获取percpu变量相当于percpu变量指针的ptr(符号地址)加上__my_cpu_offset,__my_cpu_offset宏即是从当前cpu的tpidr_el1、tpidr_el2寄存器中取出此前设置的__per_cpu_offset[cpu]值,得到这个percpu变量。能使用__per_cpu_offset[cpu]获取到percpu变量,是因为在初始化静态percpu的时候使用了这个数组。具体的实现过程可以看上面引用的博客。
perCPU变量:__entry_task
该变量已经在内核中静态定义为percpu变量:
arch/arm64/kernel/process.c:
// 该宏是内核中用于声明和定义 per-CPU 变量的宏。这些宏允许内核在每个 CPU 上拥有独立的变量副本
// __attribute__((section(".data..percpu"))) int per_cpu_n。这意味着我们在 .data..percpu 段有了一个 per_cpu_n 变量
DEFINE_PER_CPU(struct task_struct *, __entry_task);
static void entry_task_switch(struct task_struct *next)
{
__this_cpu_write(__entry_task, next);
}
首先定义了__entry_task这个percpu变量(其实是一片连续内存,每个cpu都有一个对应的offset从这个percpu对应的内存中取到本cpu id号对应的变量)。然后在调用entry_task_switch()时,通过__this_cpu_write()函数修改本cpu的percpu变量值,从而记录next进程的task_struct。该函数在__switch_to()函数中调用,进程切换函数:
arch/arm64/kernel/process.c:
__notrace_funcgraph struct task_struct *__switch_to(struct task_struct *prev,
struct task_struct *next)
{
struct task_struct *last;
...
...
entry_task_switch(next);
...
...
last = cpu_switch_to(prev, next);
return last;
}