Linux操作系统,特别是针对SMP或者NUMA架构的多CPU系统的时候,描述每个CPU的私有数据的时候,Linux操作系统提供了per_cpu机制。
1.1 定义
per_cpu机制就是让每个CPU都有自己的私有数据段,便于保护与访问。
相关宏定义在include/linux/percpu-defs.h文件中:
/*
* Normal declaration and definition macros.
*/
#define DECLARE_PER_CPU_SECTION(type, name, sec) \
extern __PCPU_ATTRS(sec) __typeof__(type) name
#define DEFINE_PER_CPU_SECTION(type, name, sec) \
__PCPU_ATTRS(sec) PER_CPU_DEF_ATTRIBUTES \
__typeof__(type) name
#endif
/*
* Variant on the per-CPU variable declaration/definition theme used for
* ordinary per-CPU variables.
*/
#define DECLARE_PER_CPU(type, name) \
DECLARE_PER_CPU_SECTION(type, name, "")
#define DEFINE_PER_CPU(type, name) \
DEFINE_PER_CPU_SECTION(type, name, "")
include/linux/percpu-defs.h文件
#define __PCPU_ATTRS(sec) \
__percpu __attribute__((section(PER_CPU_BASE_SECTION sec))) \
PER_CPU_ATTRIBUTES
这个__PCPU_ATTRS是每个CPU变量声明和定义的最基本实现。总之就是创建CPU的一个私有变量。其中#define PER_CPU_BASE_SECTION ".data..percpu"定义其存放的数据段,定义在arch/ia64/include/asm/percpu.h文件中。
1.2 示例
例如定义描述每个IA64CPU信息的数据结构变量ia64_cpu_info, 在文件
arch/ia64/include/asm/processor.h,DEFINE_PER_CPU,定义这种私有数据,放在特定的数据段中。
DECLARE_PER_CPU(struct cpuinfo_ia64, ia64_cpu_info);
在文件arch/ia64/include/asm/processor.h中,定义了结构体cpuinfo_ia64。其中定义了CPU类型,硬件BUG标志, CPU状态等。
struct cpuinfo_ia64 {
unsigned int softirq_pending;
unsigned long itm_delta; /* # of clock cycles between clock ticks */
unsigned long itm_next; /* interval timer mask value to use for next clock tick */
unsigned long nsec_per_cyc; /* (1000000000<<IA64_NSEC_PER_CYC_SHIFT)/itc_freq */
unsigned long unimpl_va_mask; /* mask of unimplemented virtual address bits (from PAL) */
unsigned long unimpl_pa_mask; /* mask of unimplemented physical address bits (from PAL) */
unsigned long itc_freq; /* frequency of ITC counter */
unsigned long proc_freq; /* frequency of processor */
unsigned long cyc_per_usec; /* itc_freq/1000000 */
unsigned long ptce_base;
unsigned int ptce_count[2];
unsigned int ptce_stride[2];
struct task_struct *ksoftirqd; /* kernel softirq daemon for this CPU */
#ifdef CONFIG_SMP
unsigned long loops_per_jiffy;
int cpu;
unsigned int socket_id; /* physical processor socket id */
unsigned short core_id; /* core id */
unsigned short thread_id; /* thread id */
unsigned short num_log; /* Total number of logical processors on
* this socket that were successfully booted */
unsigned char cores_per_socket; /* Cores per processor socket */
unsigned char threads_per_core; /* Threads per core */
#endif
/* CPUID-derived information: */
unsigned long ppn;
unsigned long features;
unsigned char number;
unsigned char revision;
unsigned char model;
unsigned char family;
unsigned char archrev;
char vendor[16];
char *model_name;
#ifdef CONFIG_NUMA
struct ia64_node_data *node_data;
#endif
};
1.3 初始化
在start_kernel函数中调用执行函数setup_per_cpu_areas( ),其会将.data.percpu中的数据拷贝到每个CPU的数据段中,每个CPU一份。 其中CPU n 对应的专有数据区的首地址为__per_cpu_offset[n]。
1.4 读取变量
在不同的内核版本中,略有差异,这里取的是最新的2018/3/19日的主线版本中代码。include/linux/percpu-defs.h
#define get_cpu_var(var) \
(*({ \
preempt_disable(); \
this_cpu_ptr(&var); \
}))