转自 http://www./linux/clf/linuxK/archive/00/00/47/91/479165.html 首先,在arch/i386/kernel/vmlinux.lds中有
/*will be free 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*/ 并且,整个.data.percpu这个section都在__init_begin和__init_end之间, unsigned long __per_cpu_offset[NR_CPUS]; static void __init setup_per_cpu_areas(void) { unsigned long size, i; char *ptr; /* Created by linker magic */ extern char __per_cpu_start[], __per_cpu_end[]; /* Copy section for each CPU (we discard the original) */ 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的变量 /* This macro obfuscates arithmetic on a variable address so that gcc shouldn't recognize the original var, and make assumptions about it */ #define RELOC_HIDE(ptr, off) ({ unsigned long __ptr; __asm__ ("" : "=g"(__ptr) : "0"(ptr)); (typeof(ptr)) (__ptr + (off)); }) /* var is in discarded region: offset to particular copy we want */ #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()便访问到了不同的变量。 --END
|
|
来自: linux_android > 《linux机制》