Chinaunix首页 | 论坛 | 博客
  • 博客访问: 462451
  • 博文数量: 185
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 681
  • 用 户 组: 普通用户
  • 注册时间: 2011-08-06 21:45
个人简介

为梦而战

文章分类

全部博文(185)

文章存档

2016年(3)

2015年(103)

2014年(79)

我的朋友

分类: LINUX

2014-06-22 22:04:05

在Linux操作系统中,特别是针对SMP或者NUMA架构的多CPU系统的时候,描述每个CPU的私有数据的时候,Linux操作系统提供了per_cpu机制。 

per_cpu机制就是让每个CPU都有自己的私有数据段,便于保护与访问。 

通过宏DEFINE_PER_CPU,定义这种私有数据,只不过这种私有数据放在特定的数据段中。 
#define DEFINE_PER_CPU(type, name)     \ 
__attribute__((__section__(".data.percpu")))   \ 
PER_CPU_ATTRIBUTES __typeof__(type) per_cpu__##name 
在GCC中__typeof__相当于typeof(检查数据类型),用__typeof__为了兼容性。 

例如1:在Linux操作系统中,用于描述每个IA64CPU信息的数据结构的定义: 
DEFINE_PER_CPU(struct cpuinfo_ia64, cpu_info); 

例如2:描述每个CPU的状态的变量的定义:* 
* State for each CPU 
*/ 
DEFINE_PER_CPU(int, cpu_state); 

系统如何为每个CPU保留这些私有数据的? 
在start_kernel函数中调用执行函数setup_per_cpu_areas( ),setup_per_cpu_areas( )定义如下: 
static void __init setup_per_cpu_areas(void) 

unsigned long size, i; 
char *ptr; 
unsigned long nr_possible_cpus = num_possible_cpus(); 
/* Copy section for each CPU (we discard the original) */ 
size = ALIGN(PERCPU_ENOUGH_ROOM, PAGE_SIZE); 
ptr = alloc_bootmem_pages(size * nr_possible_cpus); 
for_each_possible_cpu(i) { 
   __per_cpu_offset = ptr - __per_cpu_start; 
   memcpy(ptr, __per_cpu_start, __per_cpu_end - __per_cpu_start); 
   ptr += size; 
         } 
} 
setup_per_cpu_areas( )的功能就是将.data.percpu中的数据拷贝到每个CPU的数据段中,每个CPU一份。 
其中CPU n 对应的专有数据区的首地址为__per_cpu_offset[n]。 

在Linux操作系统中,在系统启动的时候会生成以__per_cpu_start标识开头和__per_cpu_end标识结尾的数据段,在执行完函数setup_per_cpu_areas( )后,将.data.percpu中的数据拷贝到每个CPU数据段后,这个数据段就会释放(free)掉。 
.data.percpu定义如下(在arch/ia64/kernel/vmlinux.lds.S): 

   __per_cpu_start = .; 
   *(.data.percpu) 
   *(.data.percpu.shared_aligned) 
   __per_cpu_end = .; 
} 

如何存取每个CPU的这些私有数据呢? 
第一种方法:取指定cpu的指定变量 
/* 
* A percpu variable may point to a discarded regions. The following are 
* established ways to produce a usable pointer from the percpu variable 
* offset. 
*/ 
#define per_cpu(var, cpu) \ 
(*SHIFT_PERCPU_PTR(&per_cpu_var(var), per_cpu_offset(cpu))) 

| 
#define SHIFT_PERCPU_PTR(__p, __offset) RELOC_HIDE((__p), (__offset)) 
                                                                        | 
                                                                         | 
                                                                       #define RELOC_HIDE(ptr, off)     \ 
   ({ unsigned long __ptr;     \ 
     __asm__ ("" : "=r"(__ptr) : "0"(ptr));   \ 
     (typeof(ptr)) (__ptr + (off)); }) 
第二种方法:取当前CPU的指定变量 
#define __get_cpu_var(var) \ 
(*SHIFT_PERCPU_PTR(&per_cpu_var(var), my_cpu_offset)) 
第三种方法:读后写的方式读取当前CPU的制定变量 
#define __raw_get_cpu_var(var) \ 
(*SHIFT_PERCPU_PTR(&per_cpu_var(var), __my_cpu_offset))
阅读(1780) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~