linux -- per-CPU变量

per-CPU变量

per-CPU变量是一种存在与每个CPU本地的变量,对于每一种per-CPU变量,每个CPU在本地都有一份它的副本。

per-CPU变量的优点

  • 多处理器系统(smp)中无需考虑与其他处理器的竞争问题(并非绝对的)
  • 可以利用处理器本地的cache硬件,提高访问速度

per-CPU变量的分类

按照分配内存空间的类型来看,有两种:

  • 静态per-CPU变量
  • 动态per-CPU变量
    静态的per-CPU变量在编译期就静态分配,动态per-CPU变量在程序运行时动态分配。

per-CPU变量的实现机制

静态per-CPU变量

相关宏

c 复制代码
  90 #ifndef PER_CPU_BASE_SECTION
  91 #ifdef CONFIG_SMP
  92 #define PER_CPU_BASE_SECTION ".data..percpu"
  93 #else
  94 #define PER_CPU_BASE_SECTION ".data"
  95 #endif
  96 #endif

定义和声明

c 复制代码
   4/*
   5 * Base implementations of per-CPU variable declarations and definitions, where
   6 * the section in which the variable is to be placed is provided by the
   7 * 'sec' argument.  This may be used to affect the parameters governing the
   8 * variable's storage.
   9 *
/* 
per-CPU变量声明和定义的基础实现,变量被放置的section由sec参数提供,这被用于影响管理参数变量的存储方式

注意!用于声明和定义的sections必须匹配,以免由于编译器生成错误的代码来访问该节而发生链接错误。
*/
  10 * NOTE!  The sections for the DECLARE and for the DEFINE must match, lest
  11 * linkage errors occur due the compiler generating the wrong code to access
  12 * that section.
  13 */
  14#define __PCPU_ATTRS(sec)                                               \
  15        __percpu __attribute__((section(PER_CPU_BASE_SECTION sec)))     \
  16        PER_CPU_ATTRIBUTES
  17
  18#define __PCPU_DUMMY_ATTRS                                              \
  19        __attribute__((section(".discard"), unused))

  20#ifdef HAVE_MODEL_SMALL_ATTRIBUTE
  21# define PER_CPU_ATTRIBUTES     __attribute__((__model__ (__small__)))
  22#endif

  73 * Normal declaration and definition macros.
  74 */
  75#define DECLARE_PER_CPU_SECTION(type, name, sec)                        \
  76        extern __PCPU_ATTRS(sec) __typeof__(type) name
  77
  78#define DEFINE_PER_CPU_SECTION(type, name, sec)                         \
  79        __PCPU_ATTRS(sec) PER_CPU_DEF_ATTRIBUTES                        \
  80        __typeof__(type) name
  81#endif

  83/*
  84 * Variant on the per-CPU variable declaration/definition theme used for
  85 * ordinary per-CPU variables.
  86 */
  87#define DECLARE_PER_CPU(type, name)                                     \
  88        DECLARE_PER_CPU_SECTION(type, name, "")
  90#define DEFINE_PER_CPU(type, name)                                      \
  91        DEFINE_PER_CPU_SECTION(type, name, "")

这么多宏,最后其实就是DEFINE_PER_CPU和DECLARE_PER_CPU最重要。

举个例子:

c 复制代码
DECLARE_PER_CPU(int, python);
DEFINE_PER_CPU(int, python);  

被展开为:

c 复制代码
extern __percpu __attribute__((section("data..percou"))) int python;
__percpu __attribute__((section("data..percou"))) int python;

这里的定义实际把python变量放到.data...percpu段里。

看链接脚本:kernel/vmlinux.lds

定义了percpu区域的输出段,简单版本,需要对齐

所有percpu变量都会放到这个段里,__per_cpu_start和__per_cpu_end分别用于标识该区域的起始地址与终止地址。

在C代码中使用外部变量:extern char __per_cpu_load[], __per_cpu_start[], __per_cpu_end[];来引用他们。

c 复制代码
 108        INIT_TEXT_SECTION(PAGE_SIZE)
 109        . = ALIGN(16);
 110        INIT_DATA_SECTION(16)
 111        PERCPU(4)

 676/**
 677 * PERCPU - define output section for percpu area, simple version
 678 * @align: required alignment
 679 *
 680 * Align to @align and outputs output section for percpu area.  This
 681 * macro doesn't maniuplate @vaddr or @phdr and __per_cpu_load and
 682 * __per_cpu_start will be identical.
 683 *
 684 * This macro is equivalent to ALIGN(align); PERCPU_VADDR( , ) except
 685 * that __per_cpu_load is defined as a relative symbol against
 686 * .data.percpu which is required for relocatable x86_32
 687 * configuration.
 688 */
 689#define PERCPU(align)                                                   \
 690        . = ALIGN(align);                                               \
 691        .data.percpu    : AT(ADDR(.data.percpu) - LOAD_OFFSET) {        \
 692                VMLINUX_SYMBOL(__per_cpu_load) = .;                     \
 693                VMLINUX_SYMBOL(__per_cpu_start) = .;                    \
 694                *(.data.percpu.first)                                   \
 695                *(.data.percpu.page_aligned)                            \
 696                *(.data.percpu)                                         \
 697                *(.data.percpu.shared_aligned)                          \
 698                VMLINUX_SYMBOL(__per_cpu_end) = .;                      \
 699        }

per-CPU变量使用案例

一个非常典型的案例:计数器

per-CPU变量非常适合做统计计数,内核专门有一个给予per-CPU变量设计的计数器(lib/percpu-counter.c)

比如在网络子系统中,要计算系统接受到的各类网络包的数量,这些包更新的频率是极快的,这就需要percpu的支持,系统中每个处理器都有这么一个对网络包数量进行计量的副本,变量更新时无需考虑多处理器竞争问题,想算出总数是只需将所有处理器的同一percpu副本相加即可。

相关推荐
LCG元1 小时前
Linux 日志分析全攻略:快速从海量日志中定位问题
linux
_Power_Y1 小时前
Linux&git入门&设计模式(常考点)
linux·git·设计模式
海蓝可知天湛1 小时前
Ubuntu24.10禁用该源...+vmware无法复制黏贴“天坑闭环”——从 DNS 诡异解析到 Ubuntu EOL 引发的 apt 404排除折腾记
linux·服务器·安全·ubuntu·aigc·bug
vvw&1 小时前
如何在 Ubuntu 24.04 上安装和使用 AdGuard
linux·运维·服务器·ubuntu·adguard
遇见火星2 小时前
Linux 网络配置实战:RHEL/CentOS 7+ 永久静态路由配置与优先级调整全攻略
linux·网络·centos·静态路由·centos 7
安审若无3 小时前
linux怎么检查磁盘是否有坏道
linux·运维·服务器
HalvmånEver3 小时前
Linux的第二章 : 基础的指令(二)
linux·运维·服务器·开发语言·学习
大梦南柯3 小时前
linux创建网站
linux·运维·服务器
刘永鑫Adam3 小时前
代码管理及Linux模拟工具Git for Windows安装使用教程
linux·运维·服务器·git
孙同学_3 小时前
【Linux篇】信号从哪来?到哪去?—— Linux信号的产生方式与保存机制
linux·运维·服务器