Linux内核中的Per-CPU变量无锁并发编程作为一名深耕操作系统和嵌入式开发的工程师我对Linux内核中的Per-CPU变量机制有着深入的理解。Per-CPU变量是一种重要的优化技术通过为每个CPU分配独立的数据副本避免了多核之间的缓存同步开销。Per-CPU变量的基本概念Per-CPU变量的核心思想是数据隔离每个CPU拥有独立的数据副本无锁访问不需要锁即可安全访问缓存友好数据位于CPU本地缓存减少缓存一致性开销Per-CPU变量的定义和访问1. 静态定义// 定义Per-CPU变量 DEFINE_PER_CPU(int, my_counter); DEFINE_PER_CPU(struct my_struct, my_data); // 定义Per-CPU数组 DEFINE_PER_CPU(int[NR_CPUS], cpu_stats);2. 动态分配// 动态分配Per-CPU变量 void __percpu *ptr alloc_percpu(int); if (!ptr) return -ENOMEM; // 释放 free_percpu(ptr);3. 访问Per-CPU变量// 获取当前CPU的变量 int *counter get_cpu_var(my_counter); (*counter); put_cpu_var(my_counter); // 简化版本禁用抢占 preempt_disable(); __this_cpu_inc(my_counter); preempt_enable(); // 访问特定CPU的变量 int cpu 0; int value per_cpu(my_counter, cpu); per_cpu(my_counter, cpu) value 1;实现原理// Per-CPU变量在内存中的布局 // CPU0: [变量0] // CPU1: [变量1] // CPU2: [变量2] // ... // 获取当前CPU变量的偏移 #define get_cpu_var(var) \ (*({ \ preempt_disable(); \ __get_cpu_var(var); \ })) // 实际访问 #define __get_cpu_var(var) \ (*SHIFT_PERCPU_PTR((var), __my_cpu_offset))使用场景1. 统计计数器DEFINE_PER_CPU(unsigned long, page_alloc_count); void alloc_page_stats(void) { // 无锁增加计数器 __this_cpu_inc(page_alloc_count); } unsigned long get_total_alloc_count(void) { unsigned long total 0; int cpu; for_each_possible_cpu(cpu) { total per_cpu(page_alloc_count, cpu); } return total; }2. 网络包处理struct softnet_data { struct list_head poll_list; struct sk_buff_head process_queue; // ... }; DEFINE_PER_CPU(struct softnet_data, softnet_data); void net_rx_action(struct softnet_data *sd) { // 处理本CPU的网络包 struct sk_buff *skb; while ((skb __skb_dequeue(sd-process_queue))) { process_packet(skb); } }3. 内存分配器struct kmem_cache_cpu { void **freelist; struct page *page; // ... }; struct kmem_cache { struct kmem_cache_cpu __percpu *cpu_slab; // ... }; void *kmem_cache_alloc(struct kmem_cache *s, gfp_t flags) { struct kmem_cache_cpu *c raw_cpu_ptr(s-cpu_slab); void *object c-freelist; if (object) { c-freelist get_freepointer(s, object); return object; } // 本地缓存为空从共享缓存获取 return __kmem_cache_alloc(s, flags); }注意事项1. 禁用抢占访问Per-CPU变量时需要禁用抢占// 正确做法 preempt_disable(); __this_cpu_inc(my_counter); preempt_enable(); // 或者使用get_cpu_var/put_cpu_var int *ptr get_cpu_var(my_counter); (*ptr); put_cpu_var(my_counter);2. 数据一致性// 错误可能在访问过程中发生抢占迁移 int value __this_cpu_read(my_counter); // -- 抢占发生CPU可能改变 __this_cpu_write(my_counter, value 1); // 正确使用原子操作或禁用抢占 this_cpu_inc(my_counter); // 内部禁用抢占3. 跨CPU访问// 访问其他CPU的变量需要小心 int cpu 1; // 确保CPU在线 if (cpu_online(cpu)) { int value per_cpu(my_counter, cpu); // 注意值可能立即过时 }性能优化建议优先使用本地访问避免频繁访问其他CPU的数据批量处理减少禁用抢占的时间合理对齐利用缓存行对齐避免伪共享避免频繁跨CPU汇总定期而非实时汇总统计信息总结Per-CPU变量是Linux内核中实现无锁并发的重要技术通过数据隔离避免了昂贵的缓存同步开销。作为嵌入式开发者合理使用Per-CPU变量可以显著提升多核系统的性能。