Linux内核 -- 高性能运算操作之 this_cpu_* 接口

news/2024/10/21 23:53:10/

Linux this_cpu_* 接口的作用与用法

Linux 内核中,this_cpu_* 接口用于访问和操作每个 CPU 独有的 per-CPU 变量,提供了一组高效的方法来处理 per-CPU 数据。在 SMP(对称多处理)系统中,per-CPU 变量可以有效地减少缓存一致性问题,提高性能,因为每个 CPU 维护独立的数据副本。

this_cpu_* 接口的主要作用是通过汇编指令对当前 CPU 上的 per-CPU 变量进行访问和操作。这样做的好处是减少访问数据时的锁竞争,提高内核代码的并发性能。常用的接口包括 this_cpu_read()this_cpu_write()this_cpu_add()this_cpu_sub()this_cpu_inc()this_cpu_dec() 等。

一、this_cpu_* 接口的作用

  1. 快速访问当前 CPU 的 per-CPU 变量:每个 CPU 上的数据都是独立的,因此在不需要加锁的情况下,可以提高数据访问的速度。
  2. 减少锁竞争:在操作 per-CPU 数据时,避免了全局锁的使用,从而减少了 CPU 之间的同步开销和锁争用。
  3. 高效的汇编实现this_cpu_* 接口通常会使用内嵌汇编实现,以确保操作的原子性。这些操作包括加法、减法、取值等操作。

二、常用的 this_cpu_* 接口

以下是一些常用的 this_cpu_* 接口及其功能:

  1. this_cpu_ptr()

    • 获取当前 CPU 的 per-CPU 变量的指针。
    • 用法示例:
      int *ptr = this_cpu_ptr(&some_percpu_var);
      
  2. this_cpu_read()

    • 读取当前 CPU 的 per-CPU 变量的值。
    • 用法示例:
      int value = this_cpu_read(some_percpu_var);
      
  3. this_cpu_write()

    • 写入一个值到当前 CPU 的 per-CPU 变量。
    • 用法示例:
      this_cpu_write(some_percpu_var, 10);
      
  4. this_cpu_add()

    • 对当前 CPU 的 per-CPU 变量执行加法操作。
    • 用法示例:
      this_cpu_add(some_percpu_var, 5);
      
  5. this_cpu_sub()

    • 对当前 CPU 的 per-CPU 变量执行减法操作。
    • 用法示例:
      this_cpu_sub(some_percpu_var, 3);
      
  6. this_cpu_inc()

    • 增加当前 CPU 的 per-CPU 变量的值(自增 1)。
    • 用法示例:
      this_cpu_inc(some_percpu_var);
      
  7. this_cpu_dec()

    • 减少当前 CPU 的 per-CPU 变量的值(自减 1)。
    • 用法示例:
      this_cpu_dec(some_percpu_var);
      
  8. this_cpu_xchg()

    • 原子地交换当前 CPU 的 per-CPU 变量的值。
    • 用法示例:
      int old_value = this_cpu_xchg(some_percpu_var, new_value);
      
  9. this_cpu_cmpxchg()

    • 执行一个原子比较并交换操作(compare and exchange)。
    • 用法示例:
      int old_value = this_cpu_cmpxchg(some_percpu_var, old_value, new_value);
      

三、使用场景

  1. 统计信息的收集

    • 在多核系统中,使用 per-CPU 变量进行统计可以减少锁竞争。例如,内核统计网络包的收发数量,使用 this_cpu_add() 更新计数器。
  2. 中断处理

    • 中断上下文中通常不适合使用锁,因此可以使用 this_cpu_* 接口来处理 per-CPU 的中断统计,确保中断处理的高效性和低延迟。
  3. 内存分配

    • 在内核的 slab 分配器中,使用 per-CPU 变量来维护 CPU 本地的缓存池,以加速内存的分配和释放。

四、代码示例

以下是一个简单的例子,演示如何使用 this_cpu_* 接口来操作 per-CPU 变量:

#include <linux/percpu.h>
#include <linux/module.h>
#include <linux/kernel.h>DEFINE_PER_CPU(int, my_percpu_var);static int __init my_module_init(void)
{int cpu;// 初始化每个 CPU 的 per-CPU 变量for_each_possible_cpu(cpu) {per_cpu(my_percpu_var, cpu) = 0;}// 在当前 CPU 上增加 per-CPU 变量的值this_cpu_add(my_percpu_var, 1);pr_info("Current CPU %d, my_percpu_var: %d\n", smp_processor_id(), this_cpu_read(my_percpu_var));return 0;
}static void __exit my_module_exit(void)
{int cpu;// 打印每个 CPU 的 per-CPU 变量值for_each_possible_cpu(cpu) {pr_info("CPU %d, my_percpu_var: %d\n", cpu, per_cpu(my_percpu_var, cpu));}
}module_init(my_module_init);
module_exit(my_module_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("Example Author");
MODULE_DESCRIPTION("A simple example of using this_cpu_* interface");

五、注意事项

  1. 原子性this_cpu_* 系列接口在实现上通常使用汇编来确保原子性,以避免在多处理器环境下出现竞态条件。比如 this_cpu_add(),其实现确保了加法操作在多个 CPU 上是线程安全的。

  2. 效率:由于 this_cpu_* 接口是基于当前 CPU 的,因此它们不需要访问其他 CPU 的缓存,也不需要获取锁,能够显著提高效率。特别是在频繁操作的场景下,per-CPU 数据能减少缓存一致性协议带来的开销。

  3. 适用场景:这些接口只适用于 per-CPU 变量,不能用于全局共享的数据。如果需要在多个 CPU 之间共享数据,则应使用适当的同步机制,如自旋锁或读写锁。

六、总结

this_cpu_* 系列接口是 Linux 内核中用于操作 per-CPU 变量的强大工具。它们提供了高效的 per-CPU 数据访问方法,适用于计数、统计等无需在多个 CPU 之间共享的数据。通过使用这些接口,开发人员可以提高内核代码的并发性和执行效率,特别是在 SMP 系统中。


http://www.ppmy.cn/news/1540930.html

相关文章

Docker-nginx数据卷挂载

数据卷&#xff08;volume&#xff09;是一个虚拟目录&#xff0c;是容器内目录与宿主机目录之间映射的桥梁。 以Nginx为例&#xff0c;我们知道Nginx中有两个关键的目录&#xff1a; html&#xff1a;放置一些静态资源conf&#xff1a;放置配置文件 如果我们要让Nginx代理我们…

基于Multisim8路彩灯循环控制电路设计与仿真

1&#xff0e;彩灯能够自动循环点亮&#xff1b; 2&#xff0e;彩灯循环频率快慢可调&#xff1b; 3&#xff0e;彩灯具有8路输出。 4&#xff0e;自行设计脉冲信号产生电路。 链接&#xff1a;https://pan.baidu.com/s/1PhpVy58Y6-_uXnie8KYyzg 提取码&#xff1a;zjad

STM32L010F4 最小系统设计

画一个 STM32L010F4 的测试板子...... by 矜辰所致前言 最近需要用到一个新的 MCU&#xff1a; STM32L010F4 &#xff0c;上次测试的 VL53L0X 需要移植到这个芯片上&#xff0c;网上一搜 STM32L010F4&#xff0c;都是介绍资料&#xff0c;没有最小系统&#xff0c;使用说明等。…

使用Vscode配置ftp连接远程服务器(上传本地文件)

1.安装插件 扩展商店搜sftp,点击进行安装。 2.配置json文件 crtl+shift+p 输入ftp配置命令 sftp:config {"name": "My Server", //设置名字"host": "localhost"</

uniapp上如何绑定全局事件总线(引入自定义全局组件例如弹窗)

1、在main.js中挂载bus // main.js Vue.prototype.$bus new Vue(); uni.$bus Vue.prototype.$bus; // 确保在 uni 上绑定 2、写一个全局弹窗组件 <template><view v-if"visible" class"toast-container"><view class"icon-conta…

鸿蒙网络编程系列12-使用Request部件下载文件到本地示例

1. Request下载文件简介 在前述文章鸿蒙网络编程系列10-使用HttpRequest下载文件到本地示例中&#xff0c;使用基础的HttpRequest模块演示了如何下载文件到本地&#xff0c;整个下载过程看起来好像不太复杂&#xff0c;但是&#xff0c;如果考虑到可靠性、网络问题以及文件类型…

图像处理中的图像重建

图像重建是指通过对观测到的图像或图像数据进行处理和分析&#xff0c;以恢复出原始或高质量的图像。图像重建常用于图像压缩、图像增强、图像修复等应用领域。 以下是一些常见的图像重建方法&#xff1a; 插值法&#xff08;Interpolation&#xff09;&#xff1a;插值法是一…

【从零开始的LeetCode-算法】884. 两句话中的不常见单词

句子 是一串由空格分隔的单词。每个 单词 仅由小写字母组成。 如果某个单词在其中一个句子中恰好出现一次&#xff0c;在另一个句子中却 没有出现 &#xff0c;那么这个单词就是 不常见的 。 给你两个 句子 s1 和 s2 &#xff0c;返回所有 不常用单词 的列表。返回列表中单词…