Chinaunix首页 | 论坛 | 博客
  • 博客访问: 98954
  • 博文数量: 28
  • 博客积分: 1435
  • 博客等级: 上尉
  • 技术积分: 265
  • 用 户 组: 普通用户
  • 注册时间: 2010-04-26 11:40
文章分类

全部博文(28)

文章存档

2017年(1)

2012年(1)

2011年(6)

2010年(20)

我的朋友

分类: LINUX

2011-04-06 19:06:47

在arch/i386/kernel/vmlinux.lds中有
  1.   /* will be freed after init */
  2.   . = ALIGN(4096);                /* Init code and data */
  3.   __init_begin = .;

  4.   /* 此处省略若干行:) */
  5.   
  6.   . = ALIGN(32);
  7.   __per_cpu_start = .;
  8.   .data.percpu  : { *(.data.percpu) }
  9.   __per_cpu_end = .;
  10.   . = ALIGN(4096);
  11.   __init_end = .;
  12.   /* 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,其实就是相对于__per_cpu_start的偏移量。

系统启动后,在start_kernel()中会调用如下函数
  1. unsigned long __per_cpu_offset[NR_CPUS];

  2. static void __init setup_per_cpu_areas(void)
  3. {
  4.         unsigned long size, i;
  5.         char *ptr;
  6.         /* Created by linker magic */
  7.         extern char __per_cpu_start[], __per_cpu_end[];

  8.         /* Copy section for each CPU (we discard the original) */
  9.         size = ALIGN(__per_cpu_end - __per_cpu_start, SMP_CACHE_BYTES);
  10. #ifdef CONFIG_MODULES
  11.         if (size < PERCPU_ENOUGH_ROOM)
  12.                 size = PERCPU_ENOUGH_ROOM;
  13. #endif

  14.         ptr = alloc_bootmem(size * NR_CPUS);

  15.         for (i = 0; i < NR_CPUS; i++, ptr += size) {
  16.                 __per_cpu_offset[i] = ptr-__per_cpu_start;
  17.                 memcpy(ptr, __per_cpu_start, __per_cpu_end - __per_cpu_start);
  18.         }
  19. }


在该函数中,为每个CPU分配一段内存,并将.data.percpu中的数据拷贝到其中,
每个CPU各有一份,其中CPU n对应的专有数据区的首地址为__per_cpu_offset[n]。
这样,前述相应于__per_cpu_start的偏移量per_cpu__runqueues就变成了相应于
__per_cpu_offset[n]的偏移量,这样.data.percpu这个section在系统初始化后
就可以释放了。

再看如何存取per cpu的变量
  1. /* This macro obfuscates arithmetic on a variable address so that gcc
  2.    shouldn't recognize the original var, and make assumptions about it */
  3. #define RELOC_HIDE(ptr, off)                                        \
  4.   ({ unsigned long __ptr;                                        \
  5.     __asm__ ("" : "=g"(__ptr) : "0"(ptr));                \
  6.     (typeof(ptr)) (__ptr + (off)); })

  7. /* var is in discarded region: offset to particular copy we want */
  8. #define per_cpu(var, cpu) (*RELOC_HIDE(&per_cpu__##var, __per_cpu_offset[cpu]))
  9. #define __get_cpu_var(var) per_cpu(var, smp_processor_id())

  10. #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,也就是说可以进行赋值操作。
这正好是上述对应CPU的专有数据区的首地址加上对应偏移量per_cpu__runqueues,

由于不同的per cpu变量有不同的偏移量,并且不同的CPU其专有数据区首地址不同,
因此,通过__get_cpu_var()便访问到了不同的变量。
阅读(499) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~