uclinux-2008r1(bf561)内核中的per_cpu

 
   
当内核不需要支持smp的时候,per_cpu是无关紧要的,因为数据只有一份。但是当需要支持smp的时候,per_cpu就显示相当重要了,它可以为每个CPU定义属于自己的专有数据。
在kernel/shed.c中有这样一个定义:
 
/*
 * This is the main, per-CPU runqueue data structure.
 *
 * Locking rule: those places that want to lock multiple runqueues
 * (such as the load balancing or the thread migration code), lock
 * acquire operations must be ordered by ascending &runqueue.
 */
struct rq {
    
};
static DEFINE_PER_CPU(struct rq, runqueues) ____cacheline_aligned_in_smp;
从注释中可以看出rq这个结构体保存了运行时的任务队列信息,当然每个CPU都有自己的任务队列,因而自然需要为每个CPU都定义一个私有的数据。
其中DEFINE_PER_CPU的定义在include/asm-generic/percpu.h中:
 
/* Separate out the type, so (int[3], foo) works. */
#define DEFINE_PER_CPU(type, name) /
    __attribute__((__section__(".data.percpu"))) __typeof__(type) per_cpu__##name
满简单的,无非就是定义一个指定类型的变量,再将它放到一个特定的数据段中。为此,我们需要在ldf文件中添加这样一段:
               //.percpu             
               INPUT_SECTION_ALIGN(64)
               . = (. + 63) / 64 * 64;
               ___per_cpu_start = .;
               INPUT_SECTIONS($LIBRARIES_UCLINUX(.data.percpu))
               ___per_cpu_end = .;
到这里,我们还看不出是如何为每个CPU保留自己的数据的。
在start_kernel()函数中有这样一个调用:
     setup_per_cpu_areas();
     这个函数实现如下所示:
unsigned long __per_cpu_offset[NR_CPUS] __read_mostly;
 
EXPORT_SYMBOL(__per_cpu_offset);
 
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[i] = ptr - __per_cpu_start;
         memcpy(ptr, __per_cpu_start, __per_cpu_end - __per_cpu_start);
         ptr += size;
     }
}
也就是说,A核从内核为自己申请了一块空间,把percpu这个段的内容复制了一遍,自然也就有了一块自己的数据。当然,当内核运行到这里的时候,B核还没有启动, for_each_possible_cpu只会执行一次,但是可以想像,当B核启动时,应该也会将这个段的内容复制一遍。
在这里,还使用了一个__per_cpu_offset数组来保存每个核的私有数据离__per_cpu_start的偏移量。
当bf561核需要访问自己的私有数据时,它会这样:
         volatile struct rq *rq;
         rq = cpu_rq(i);
这里,cpu_rq定义为:
#define cpu_rq(cpu)         (&per_cpu(runqueues, (cpu)))
而per_cpu的定义则为:
# define RELOC_HIDE(ptr, off)                      /
 ({ unsigned long __ptr;                      /
     __ptr = (unsigned long) (ptr);                /
    (typeof(ptr)) (__ptr + (off)); })
 
/* var is in discarded region: offset to particular copy we want */
#define per_cpu(var, cpu) (*({                 /
     extern int simple_identifier_##var(void); /
     RELOC_HIDE(&per_cpu__##var, __per_cpu_offset[cpu]); }))
从而保证访问的是自己的私有数据,而不是.data.per_cpu这个段中的数据内容。
 
 
 
 
 
 
 
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌云阁主

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值