linux:CPU私有变量(per

摘自:http://blog.chinaunix.net/uid-24148050-id-300576.html

一、简介2.6内核上一个新的特性就是per-CPU变量。顾名思义,就是每个处理器上有此变量的一个副本。per-CPU的最大优点就是,对它的访问几乎不需要锁,因为每个CPU都在自己的副本上工作。tasklet、timer_list等机制都使用了per-CPU技术。二、API使用注意,2.6内核是抢占式的。所以在访问per-CPU变量时,应使用特定的API来避免抢占,即避免它被切换到另一个CPU上被处理。per-CPU变量可以在编译时声明,也可以在系统运行时动态生成实例一:

    编译期间创建一个per-CPU变量:DEFINE_PER_CPU(int,my_percpu);//声明一个变量DEFINE_PER_CPU(int[3],my_percpu_array);//声明一个数组使用编译时生成的per-CPU变量: ptr = get_cpu_var(my_percpu);// 使用ptrput_cpu_var(my_percpu);//当然,也可以使用下列宏来访问特定CPU上的per-CPU变量per_cpu(my_percpu,cpu_id);//per-CPU变量导出,供模块使用:EXPORT_PER_CPU_SYMBOL(per_cpu_var);EXPORT_PER_CPU_SYMBOL_GPL(per_cpu_var);

实例二:

    动态分配per-CPU变量:void*alloc_percpu(type);void*__alloc_percpu(size_t size,size_t align);使用动态生成的per-CPU变量:intcpu; cpu=get_cpu();ptr=per_cpu_ptr(my_percpu);//使用ptrput_cpu();

三、实现使用上面的API为什么就能避免抢占问题呢,看看代码实现就知道了

    #defineget_cpu_var(var)(*({\externintsimple_identifier_##var(void);\preempt_disable();\&;__get_cpu_var(var);}))#defineput_cpu_var(var)preempt_enable()
    #defineget_cpu()({preempt_disable(); smp_processor_id(); })#defineput_cpu()preempt_enable()

关键就在于 preempt_disable 和 preempt_enable 两个调用,分别是禁止抢占和开启抢占抢占相关的东东以后再看per-cpu 变量的引入有效的解决了SMP系统中处理器对锁得竞争,每个cpu只需访问自己的本地变量。本文阐述了per-cpu变量在2.6内核上的实现和相关操作。在系统编译阶段我们就手工的定义了一份所有的per-cpu变量,这些变量的定义是通过宏DEFINE_PER_CPU实现的:

    11 #define DEFINE_PER_CPU(type,name)\12 __attribute__((__div__(".data.percpu")))__typeof__(type)per_cpu__##name

从上面的代码我们可以看出,手工定义的所有per-cpu变量都是放在.data.percpu段的。注意上面的宏只是在SMP体系结构下才如此定义。如果不是SMP结构的计算机那么只是简单的把所有的per-cpu变量放到全局变量应该放到的地方。

单CPU的per-cpu变量定义:

    27 #else/*!SMP*/2829 #define DEFINE_PER_CPU(type,name)\30 __typeof__(type)per_cpu__##name

在了解了上述代码后,我们还必须弄清楚一点:单CPU的计算机中使用的per-cpu变量就是通过上述宏定义的放在全局数据区的per-cpu变 量。而在SMP体系结构中,我们使用却不是放在.data.percpu段的变量,设想一下如果使用这个变量,那么应该哪个CPU使用呢?事实上,SMP 下,每个cpu使用的都是在.data.percpu段中的这些per-cpu变量的副本,有几个cpu就创建几个这样的副本。

在系统初始化期 间,start_kernel()函数中调用setup_per_cpu_areas()函数,用于为每个cpu的per-cpu变量副本分配空间,注意 这时alloc内存分配器还没建立起来,该函数调用alloc_bootmem函数为初始化期间的这些变量副本分配物理空间。

    332 static void __init setup_per_cpu_areas(void)/**/333{334 unsigned long size,i;335 char*ptr;336337/*Copy divforeachCPU(we discard the original)*/338 size=ALIGN(__per_cpu_end-__per_cpu_start,SMP_CACHE_BYTES);339 #ifdef CONFIG_MODULES340if(size<PERCPU_ENOUGH_ROOM)341 size=PERCPU_ENOUGH_ROOM;342 #endif343344 ptr=alloc_bootmem(size*NR_CPUS);345346for(i=0;i<NR_CPUS;i++,ptr+=size){347 __per_cpu_offset[i]=ptr-__per_cpu_start;348 memcpy(ptr,__per_cpu_start,__per_cpu_end-__per_cpu_start);349}350}351 #endif/*!__GENERIC_PER_CPU*/

上述函数,在分配好每个cpu的per-cpu变量副本所占用的物理空间的同时,也对__per_cpu_offset[NR_CPUS]数组进行了初始化用于以后找到指定CPU的这些per-cpu变量副本。

    15 #define per_cpu(var,cpu)(*RELOC_HIDE(&per_cpu__##var,__per_cpu_offset[cpu]))16 #define __get_cpu_var(var)per_cpu(var,smp_processor_id())

这两个宏一个用于获得指定cpu的per-cpu变量,另一个用于获的本地cpu的per-cpu变量,可以自己分析一下。把艰辛的劳作看作是生命的必然,

linux:CPU私有变量(per

相关文章:

你感兴趣的文章:

标签云: