linux2.6 为了方便创建和操作每个CPU数据,引进了新的操作接口,percpu(),该接口简化了创建了操作每个CPU的数据。
定义于<linux/percpu.h> 和<mm/slab.c> 中
1. 定义和声明每个CPU变量:
- DEFINE_PER_CPU(type, name);
如果需要在别处声明此变量,以防编译时的警告,可使用下面的宏:
- DECLARE_PER_CPU(type, name);
get_cpu_var(name); //返回当前处理器上的指定变量name的值, 同时将他禁止抢占;
put_cpu_var(name); //与get_cpu_var(name)相对应,重新激活抢占;
- /*
- * Must be an lvalue. Since @var must be a simple identifier,
- * we force a syntax error here if it isn't.
- */
- #define get_cpu_var(var) (*({ \
- preempt_disable(); \
- &__get_cpu_var(var); }))
- /*
- * The weird & is necessary because sparse considers (void)(var) to be
- * a direct dereference of percpu variable (var).
- */
- #define put_cpu_var(var) do { \
- (void)&(var); \
- preempt_enable(); \
- } while (0)
通过指针来操作每个CPU的数据:
get_cpu_ptr(var); --- 返回一个void类型的指针,指向CPU ptr处的数据
put_cpu_ptr(var); --- 操作完成后,重新激活内核抢占。
- #define get_cpu_ptr(var) ({ \
- preempt_disable(); \
- this_cpu_ptr(var); })
- #define put_cpu_ptr(var) do { \
- (void)(var); \
- preempt_enable(); \
- } while (0)
per_cpu(name, cpu) ; //返回别的处理器cpu上变量name的值;
4. 给系统中每个处理器分配一个指定类型的对象: alloc_percpu();
- #define alloc_percpu(type) \
- (typeof(type) __percpu *)__alloc_percpu(sizeof(type), __alignof__(type))
- /**
- * __alloc_percpu - allocate dynamic percpu area
- * @size: size of area to allocate in bytes
- * @align: alignment of area (max PAGE_SIZE)
- *
- * Allocate zero-filled percpu area of @size bytes aligned at @align.
- * Might sleep. Might trigger writeouts.
- *
- * CONTEXT:
- * Does GFP_KERNEL allocation.
- *
- * RETURNS:
- * Percpu pointer to the allocated area on success, NULL on failure.
- */
- void __percpu *__alloc_percpu(size_t size, size_t align)
- {
- return pcpu_alloc(size, align, false);
- }
- EXPORT_SYMBOL_GPL(__alloc_percpu);
size = sizeof(type);
align = __alignof__(type);
__alignof__()是gcc的一个功能,它会返回指定类型或lvalue所需的对齐字节数。
相应的释放所有处理器上指定的每个CPU数据:free_percpu();
- /**
- * free_percpu - free percpu area
- * @ptr: pointer to area to free
- *
- * Free percpu area @ptr.
- *
- * CONTEXT:
- * Can be called from atomic context.
- */
- void free_percpu(void __percpu *ptr)
- {
- void *addr;
- struct pcpu_chunk *chunk;
- unsigned long flags;
- int off;
- if (!ptr)
- return;
- addr = __pcpu_ptr_to_addr(ptr);
- spin_lock_irqsave(&pcpu_lock, flags);
- chunk = pcpu_chunk_addr_search(addr);
- off = addr - chunk->base_addr;
- pcpu_free_area(chunk, off);
- /* if there are more than one fully free chunks, wake up grim reaper */
- if (chunk->free_size == pcpu_unit_size) {
- struct pcpu_chunk *pos;
- list_for_each_entry(pos, &pcpu_slot[pcpu_nr_slots - 1], list)
- if (pos != chunk) {
- schedule_work(&pcpu_reclaim_work);
- break;
- }
- }
- spin_unlock_irqrestore(&pcpu_lock, flags);
- }
- EXPORT_SYMBOL_GPL(free_percpu);
5. get_cpu()/put_cpu() --- 获得处理器编号
get_cpu() 在返回当前处理器编号之前,先回关闭内核抢占。
put_cpu() 重新打开内核抢占。
函数定义如下:
- #define get_cpu() ({ preempt_disable(); smp_processor_id(); })
- #define put_cpu() preempt_enable()
- int cpu;
- cpu = get_cpu(); //禁止抢占内核,并将CPU设置为当前处理器。
- //对每个处理器的数据进行操作,
- put_cpu(); //使能内核抢占