Linux PER_CPU变量的理解

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
在这里插入图片描述

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值