转自 http://www.unixresources.net/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*/
这说明__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; /* 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中的数据拷贝到其中,
/* 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),将等效地扩展为