linux私有数据的好处,linux 多处理器体系中cpu私有数据的重定位

首先,在arch/i386/kernel/vmlinux.lds中有

. = ALIGN(4096);

__init_begin = .;

. = ALIGN(32);

__per_cpu_start = .;

.data.percpu : { *(.data.percpu) }

__per_cpu_end = .; . = ALIGN(4096);

__init_end = .;

这说明__per_cpu_start和__per_cpu_end标识.data.percpu这个section的开头和结尾

并且,整个.data.percpu这个section都在__init_begin和__init_end之间,

也就是说,该section所占内存会在系统启动后释放(free)掉

因为有

#define DEFINE_PER_CPU(type, name) \

__attribute__((__section__(".data.percpu"))) __typeof__(type)

per_cpu__##name

所以

static DEFINE_PER_CPU(struct runqueue, runqueues);

会扩展成

__attribute__((__section__(".data.percpu"))) __typeof__(struct

runqueue) per_cpu__runqueues;

也就是在.data.percpu这个section中定义了一个变量per_cpu__runqueues,

其类型是struct runqueue。事实上,这里所谓的变量per_cpu__runqueues,

其实就是一个偏移量,标识该变量的地址。

--------------------

其次,系统启动后,在start_kernel()中会调用如下函数

unsigned long __per_cpu_offset[NR_CPUS];

static void __init setup_per_cpu_areas(void)

{

unsigned long size, i;

char *ptr;

extern char __per_cpu_start[], __per_cpu_end[];

size = ALIGN(__per_cpu_end - __per_cpu_start, SMP_CACHE_BYTES);

#ifdef CONFIG_MODULES

if (size < PERCPU_ENOUGH_ROOM)

size = PERCPU_ENOUGH_ROOM;

#endif

ptr = alloc_bootmem(size * NR_CPUS);

for (i = 0; i < NR_CPUS; i++, ptr += size) {

__per_cpu_offset[i] = ptr - __per_cpu_start;

memcpy(ptr, __per_cpu_start, __per_cpu_end - __per_cpu_start);

}

}

在该函数中,为每个CPU分配一段专有数据区,并将.data.percpu中的数据拷贝到其中,

每个CPU各有一份。由于数据从__per_cpu_start处转移到各CPU自己的专有数据区中了,

因此存取其中的变量就不能再用原先的值了,比如存取per_cpu__runqueues

就不能再用per_cpu__runqueues了,需要做一个偏移量的调整,

即需要加上各CPU自己的专有数据区首地址相对于__per_cpu_start的偏移量。

在这里也就是__per_cpu_offset[i],其中CPU i的专有数据区相对于

__per_cpu_start的偏移量为__per_cpu_offset[i]。

这样,就可以方便地计算专有数据区中各变量的新地址,比如对于per_cpu_runqueues,

其新地址即变成per_cpu_runqueues+__per_cpu_offset[i]。

经过这样的处理,.data.percpu这个section在系统初始化后就可以释放了。

--------------------

再看如何存取per cpu的变量

#define RELOC_HIDE(ptr, off) \

({ unsigned long __ptr; \

__asm__ ("" : "=g"(__ptr) : "0"(ptr)); \

(typeof(ptr)) (__ptr + (off)); })

#define per_cpu(var, cpu) (*RELOC_HIDE(&per_cpu__##var, __per_cpu_offset[cpu]))

#define __get_cpu_var(var) per_cpu(var, smp_processor_id())

#define get_cpu_var(var) (*({ preempt_disable(); &__get_cpu_var(var); }))

对于__get_cpu_var(runqueues),将等效地扩展为

__per_cpu_offset[smp_processor_id()] + per_cpu__runqueues

并且是一个lvalue,也就是说可以进行赋值操作。

这正好是上述per_cpu__runqueues变量在对应CPU的专有数据区中的新地址。

由于不同的per cpu变量有不同的偏移量,并且不同的CPU其专有数据区首地址不同,

因此,通过__get_cpu_var()便访问到了不同的变量。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值