当前位置: 首页 > news >正文

Linux内核中的Per-CPU变量:无锁并发编程

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); // 注意:值可能立即过时 }

性能优化建议

  1. 优先使用本地访问:避免频繁访问其他CPU的数据
  2. 批量处理:减少禁用抢占的时间
  3. 合理对齐:利用缓存行对齐避免伪共享
  4. 避免频繁跨CPU汇总:定期而非实时汇总统计信息

总结

Per-CPU变量是Linux内核中实现无锁并发的重要技术,通过数据隔离避免了昂贵的缓存同步开销。作为嵌入式开发者,合理使用Per-CPU变量可以显著提升多核系统的性能。

http://www.jsqmd.com/news/593315/

相关文章:

  • 2026年全链路性能测试方案选型与实施指南
  • python zipfile
  • COMSOL合并BIC:能带计算、Q因子计算、远场偏振投影及录屏指导
  • 游戏化学习与编程实战:CodeCombat让编程学习像玩游戏一样简单
  • 抖音无水印视频批量下载全攻略:从痛点解决到高效管理
  • Netty 线程模型
  • 2026年3月实测!GEO优化厂家产品性能大揭秘,专业的GEO优化口碑推荐技术领航者深度解析 - 品牌推荐师
  • 如何用OpCore-Simplify在30分钟内完成黑苹果配置:自动化OpenCore EFI工具终极指南
  • 飞轮储能 背靠背变流器 充放电控制 并网控制 matlab/simulink仿真模型 包括机侧...
  • 4步解决Windows Defender管控难题:技术用户的系统防护配置指南
  • 第7章 运算符-7.2 赋值运算符
  • MaaYuan自动化辅助工具高效配置避坑指南:零基础入门三步完成环境部署
  • CQUPT 2025级 数据科学与大数据技术英才班 周测#02
  • Java 开发者零成本构建 RAG 知识库:Spring AI Alibaba + Ollama 搭建本地 RAG 知识库
  • 5步快速上手:用Ryujinx在PC上完美运行Switch游戏终极指南
  • Node.js——dns模块
  • 第26课:Qt 接管 MISC、input 与定时器,把事件流和倒计时界面一起跑通
  • NSudo完全指南:轻松获取Windows最高权限的5种方法
  • 告别B站资源无法保存的烦恼:BiliTools跨平台工具箱完整使用指南
  • nacos
  • 第27课:Qt 看门狗倒计时实战,学会让界面节奏和系统守护对齐
  • 从安装到首次提交,在快马平台实战演练中掌握git核心工作流
  • 体验AI结对编程:让快马成为你的智能代码审查与优化助手
  • 输入可视化革命:如何用input-overlay消除直播中的操作信息差
  • 【GitHub项目推荐--OpenSpace:从地球到深空,打开宇宙的“数字窗口”】⭐
  • 终极图像查看器Nomacs:专业级功能完全指南
  • 数据库集群中的bond1接口出现网络丢包
  • 实战指南:用快马生成altium designer数据采集板卡全流程设计项目
  • RePKG:5个高效技巧助你掌握Wallpaper Engine资源处理与格式转换
  • 第28课:Qt 读系统时钟并响应中断,让时间界面和板级事件同时在线