1)结合上一篇x86 SWAPGS一起看;
2)x86架构,64位长模式下;
// <arch/x86/include/asm/percpu.h >
#define __percpu_seg gs
#define PER_CPU_VAR(var) %__percpu_seg:var
由此可见,通过%gs寄存器来寻址per_cpu变量。
PER_CPU变量的初始化流程总结为:
1、setup_per_cpu_areas()建立per_cpu变量区域,包括静态per_cpu区域、动态per_cpu区域和保留区域。其中,__per_cpu_offset[cpu]数组保存着每个cpu per_cpu区域的基址;
2、采用 %gs:offset 这种寻址方式访问per_cpu变量,其中%gs基址保存在MSR_GS_BASE中;
3、load_percpu_segment(int cpu)将per_cpu.gs_base赋值给每个cpu的MSR_GS_BASE;【遗留问题:per_cpu.gs_base是何处初始化赋值的没找到】;
1、定义
静态per-cpu变量通过DEFINE_PER_CPU和DECLARE_PER_CPU宏在内核源码中定义,详见如下。
// <include/linux/percpu-defs.h>
#define DECLARE_PER_CPU(type, name) \
DECLARE_PER_CPU_SECTION(type, name, "")
#define DEFINE_PER_CPU(type, name) \
DEFINE_PER_CPU_SECTION(type, name, "")
#define DEFINE_PER_CPU_SECTION(type, name, sec) \
__PCPU_ATTRS(sec) PER_CPU_DEF_ATTRIBUTES \
__typeof__(type) name
#define __PCPU_ATTRS(sec) \
__percpu __attribute__((section(PER_CPU_BASE_SECTION sec))) \
PER_CPU_ATTRIBUTES
// <include/asm-generic/percpu.h>
#ifndef PER_CPU_BASE_SECTION
#ifdef CONFIG_SMP
#define PER_CPU_BASE_SECTION ".data..percpu"
#else
#define PER_CPU_BASE_SECTION ".data"
#endif
#endif
#ifndef PER_CPU_ATTRIBUTES
#define PER_CPU_ATTRIBUTES
#endif
#ifndef PER_CPU_DEF_ATTRIBUTES
#define PER_CPU_DEF_ATTRIBUTES
#endif
//<include/linux/compiler.h>
# define __percpu
至此,DEFINE_PER_CPU(type, name)展开为
#define DEFINE_PER_CPU(type, name) \
__attribute__((section(".data..percpu"))) (type) name
查看链接脚本可知section(“.data…percpu”)的定义,如下:
//<arch/x86/kernel/vmlinux.lds>
/* Init code and data - will be freed after init */
.=ALIGN(4096);
__init_begin=.;
/*省略*/
.ALIGN(32);
__per_cpu_start=.;
.data.percpu:{*(.data.percpu)}
__per_cpu_end=.;
.=ALIGN(4096);
__init_end=.;
/*freed after init ends here*/
2、访问
访问per_cpu变量有多种方式,这里仅介绍我接触到的几种。
1、per_cpu(var, cpu)
//<include/linux/percpu-defs.h>
#define per_cpu(var, cpu) (*per_cpu_ptr(&(var), cpu))
#define per_cpu_ptr(ptr, cpu) \
({ \
__verify_pcpu_ptr(ptr); \
SHIFT_PERCPU_PTR((ptr), per_cpu_offset((cpu))); \
})
#define SHIFT_PERCPU_PTR(__p, __offset) \
RELOC_HIDE((typeof(*(__p)) __kernel __force *)(__p), (__offset))
#ifndef __per_cpu_offset
extern unsigned long __per_cpu_offset[NR_CPUS];
#define per_cpu_offset(x) (__per_cpu_offset[x])
#endif
其中,__per_cpu_offset[x]由setup_per_cpu_areas()函数初始化,这个数组保存着每个CPU的per_cpu变量的基址。
//setup_per_cpu_areas()函数暂没看懂。。。
//<include/linux/compiler.h>
#ifndef RELOC_HIDE
# define RELOC_HIDE(ptr, off) \
({ unsigned long __ptr; \
__ptr = (unsigned long) (ptr); \
(typeof(ptr)) (__ptr + (off)); })
#endif
//待更新
下图摘自:https://blog.csdn.net/wh8_2011/article/details/53138377