现代SMP操作系统使用每-CPU数据——这些数据对每个处理器都是唯一的。通常,每-CPU数据存储在一个数组中。数组中的每项对应系统上的一个可能的处理器。如:
unsigned long my_percpu[NR_CPUS];
访问的方式如下:
int cpu;
cpu = get_cpu(); /* get current processor and disable kernel preemption */
my_percpu[cpu]++; /* ... or whatever */
printk(“my_percpu on cpu=%d is %lu\n”, cpu, my_percpu[cpu]);
put_cpu(); /* enable kernel preemption */
访问每-CPU数据唯一需要考虑的就是内核抢占。内核抢占导致了两个问题,如下所示:
l 如果运行的代码被抢占并被调度到另一个处理器上运行,cpu变量不再合法,因为它指向了错误的处理器(通常,代码在获得当前处理器后不能睡眠)。
l 如果另一个任务抢占了当前运行的代码,它可能并发地访问相同处理器上的my_percpu,从而导致了一个竞态发生。
然而,任何担心都是多余的,因为get_cpu()在返回处理器编号的同时,也会禁用内核抢占。put_cpu()则恢复内核抢占。注意:如果使用smp_processor_id()来获取当前处理器的编号,内核抢占则并不会被禁止。
新的percpu接口(2.6后)
2.6内核引入了一个新的接口,称为percpu,,用于创建和操作每-CPU数据。该接口扩展了上述例子。新的接口更简单,但旧的接口依然有效。
定义:
DEFINE_PER_CPU(type, name);
声明:
DECLARE_PER_CPU(type, name);
操作变量函数:
get_cpu_var() put_cpu_var()
如:
get_cpu_var(name)++; /* increment name on this processor,同时禁用内核抢占 */
put_cpu_var(name); /* done; enable kernel preemption */
访问另一个处理器的每-CPU数据:
per_cpu(name, cpu)++; /* increment name on the given processor */,注意,该函数并没有禁用内核抢占。
动态分配每-CPU数据
内核实现了一个动态分配器,类似于kmalloc(),用于创建每-CPU数据。这些函数原型如下:
void *alloc_percpu(type); /* a macro */
void *__alloc_percpu(size_t size, size_t align);
void free_percpu(const void *);
get_cpu_var(ptr); /* return a void pointer to this processor’s copy of ptr */
put_cpu_var(ptr); /* done; enable kernel preemption */
使用例子如下:
void*percpu_ptr;
unsignedlong*foo;
percpu_ptr=alloc_percpu(unsignedlong);
if(!ptr)
/*errorallocatingmemory..*/
foo=get_cpu_var(percpu_ptr);
/*manipulatefoo..*/
put_cpu_var(percpu_ptr);