「技术干货」一文搞懂Linux内核中per-CPU变量
ahcoder 2025-01-07 10:23 11 浏览
为什么需要per-CPU变量
假设系统中有4个cpu, 同时有一个变量在各个CPU之间是共享的,每个cpu都有访问该变量的权限。
当cpu1在改变变量v的值的时候,cpu2也需要改变变量v的值。这时候就会导致变量v的值不正确。这时候机智的你就会说,在cpu1访问变量v的时候可以使用原子操作加锁,cpu2访问变量v的时候需要等待。可是机智的是否考虑过加锁对性能的影响,原子操作对cpu是极耗cpu的。
嵌入式进阶教程分门别类整理好了,看的时候十分方便,由于内容较多,这里就截取一部分图吧。
需要的朋友私信【内核】即可领取。
内核学习地址:Linux内核源码/内存调优/文件系统/进程管理/设备驱动/网络协议栈-学习视频教程-腾讯课堂
再考虑一种情况,现在高速的cpu都带有高速缓冲cache。它介于cpu和主存之间,主要作用是加快cpu的访问速度。因为主存的访问速度相比cpu读写比较慢,在之间引入cache之后,当CPU调用大量数据时,就可避开内存直接从缓存中调用,从而加快读取速度。
比如cpu1对变量v操作之后,变量v的值就发生了变化。而cpu2, cpu3, cpu4的cache中的值还是以前的值,所以这时候就需要将cpu2, cpu3, cpu4的cache中的值变为无效的,当cpu2读取变量v的时候就需要从内存中读取v。所以当某一个cpu对共享数据v做操作后,比较对其余的cache做无效操作,这也是对性能有所损耗的。
所以,就引入了per-cpu变量。
什么是per-CPU变量
per-CPU变量是linux系统一个非常有趣的特性,它为系统中的每个处理器都分配了该变量的副本。这样做的好处是,在多处理器系统中,当处理器操作属于它的变量副本时,不需要考虑与其他处理器的竞争的问题,同时该副本还可以充分利用处理器本地的硬件缓冲cache来提供访问速度。
per-CPU按照存储变量的空间来源分为静态per-CPU变量和动态per-CPU变量,前者的存储空间是在代码编译时静态分配的,而后者的存储空间则是在代码的执行期间动态分配的。
静态per-CPU变量声明和定义
声明DECLARE_PER_CPU宏:
<include/linux/percpu-defs.h>
----------------------------------------------------------------
#define DECLARE_PER_CPU(type, name)
DECLARE_PER_CPU_SECTION(type, name, "")
#define DECLARE_PER_CPU_SECTION(type, name, sec)
extern __PCPU_ATTRS(sec) __typeof__(type) name
#define __PCPU_ATTRS(sec)
__percpu __attribute__((section(PER_CPU_BASE_SECTION sec)))
PER_CPU_ATTRIBUTES
<include/asm-generic/percpu.h>
-----------------------------------------------------
#ifndef PER_CPU_BASE_SECTION
#ifdef CONFIG_SMP
#define PER_CPU_BASE_SECTION ".data..percpu"
#else
#define PER_CPU_BASE_SECTION ".data"
#endif
#endif
对上的宏定义DECLARE_PER_CPU使用例子: DECLARE_PER_CPU(int, val)来详细说明。
DECLARE_PER_CPUT(int, val)
-> DECLARE_PER_CPU_SECTION(int, val, "")
-> extern __PCPU_ATTRS("") __typeof__(int) val
-> extern __percpu __attribute__((section(".data..percpu"))) int val
从上面的分析可以看出,该宏在源代码中声明了__percpu int val变量,该变量放在一个名为”.data..percpu”的section中。
定义DEFINE_PER_CPU宏:
<include/linux/percpu-defs.h>
----------------------------------------------------------------
#define DEFINE_PER_CPU(type, name)
DEFINE_PER_CPU_SECTION(type, name, "")
#define DEFINE_PER_CPU_SECTION(type, name, sec)
__PCPU_ATTRS(sec) PER_CPU_DEF_ATTRIBUTES
__typeof__(type) name
#ifndef PER_CPU_DEF_ATTRIBUTES
#define PER_CPU_DEF_ATTRIBUTES
#endif
对以上DEFINE_PER_CPU依然使用DEFINE_PER_CPU(int, val)做举例说明:
DEFINE_PER_CPU(int, val)
-> DEFINE_PER_CPU_SECTION(int, val, "")
->__PCPU_ATTRS("") __typeof__(int) val
-> __percpu __attribute__((section(".data..percpu"))) int val
可以看到,相比与DECLARE_PER_CPU,DEFINE_PER_CPU只是去掉了变量前面的extern, 所以使用DEFINE_PER_CPU(int,val)将会在源码中定义一个变量:
__percpu __attribute__((section(".data..percpu"))) int val
以下是per-CPU所有的形式的声明和定义:
声明和定义 | 解释 |
DECALRE_PER_CPU(type, name)/DEFINE_PER_CPU(type, name) | 普通的per-CPU声明和定义 |
DECLARE_PER_CPU_FIRST(type, name)/DEFINE_PER_CPU_FIRST(type, name) | 该per-CPU变量会在整个serction的最前面,所谓的first |
DECLARE_PER_CPU_SHARED_ALIGNED(type, name)/DEFINE_PER_CPU_SHARED_ALIGNED(type, name) | 该per-CPU在SMP系统下会对齐到cache line,在UP系统下不需要对齐 |
DECLARE_PER_CPU_ALIGNED(type, name)/DEFINE_PER_CPU_ALIGNED(type, name) | 在SMP和UP系统都对齐到cache line |
DECLARE_PER_CPU_PAGE_ALIGNED(type, name)/DEFINE_PER_CPU_PAGE_ALIGNED(type, name) | 该per-CPU变量必须页对齐 |
DECLARE_PER_CPU_READ_MOSTLY(type, name)/DEFINE_PER_CPU_READ_MOSTLY(type, name) | 该per-CPU变量必须是read mostly |
静态per-CPU变量的链接脚本
在上一节per-CPU变量的声明和定义中,可以看到最后的变量都是存在一个”.data..percpu”段中。
. = ALIGN((1 << 12));
.data..percpu : AT(ADDR(.data..percpu) - 0)
{
__per_cpu_load = .;
__per_cpu_start = .;
*(.data..percpu..first) . = ALIGN((1 << 12));
*(.data..percpu..page_aligned) . = ALIGN(64);
*(.data..percpu..read_mostly) . = ALIGN(64);
*(.data..percpu)
*(.data..percpu..shared_aligned)
__per_cpu_end = .;
}
可见,内核在编译链接的时候会把所有静态定义的per-CPU变量统一放到”.data..percpu”section中。链接器生成__per_cpu_start和__per_cpu_end两个变量表示该section的起始和结束地址。
动态分配per-CPU变量
- 分配函数
#define alloc_percpu(type)
(typeof(type) __percpu *)__alloc_percpu(sizeof(type),
__alignof__(type))
根据类型type,分配per-CPU变量
- 释放函数
void free_percpu(void __percpu *ptr)
释放ptr所指向的per-CPU变量。
使用静态per-CPU变量
因为per-CPU不能像一般的变量那样访问,必须使用内核提供的函数:
#define get_cpu_var(var)
(*({
preempt_disable();
this_cpu_ptr(&var);
}))
#define put_cpu_var(var)
do {
(void)&(var);
preempt_enable();
} while (0)
因为在get_cpu_var函数中关掉了抢占,所以在put_cpu_var中需要开启抢占。显然get函数和put函数需要匹配使用。 机智的你可能会问,为什么还需要关闭抢占,因为对于per-CPU来说已经是单处理器了。但是机智的你没有想到的是,在cpu访问per-CPU的时候,突然系统发生了一次紧急抢占,这时候cpu还在处理per-CPU变量,一旦被抢占了cpu资源,可能当前进程会换出处理器。所以关闭抢走还是必要的。
如果需要访问其他处理器的副本,可以使用函数per_cpu(var, cpu)
#define per_cpu(var, cpu) (*per_cpu_ptr(&(var), cpu))
使用动态per-CPU变量
#define get_cpu_ptr(var)
({
preempt_disable();
this_cpu_ptr(var);
})
#define put_cpu_ptr(var)
do {
(void)(var);
preempt_enable();
} while (0)
#define per_cpu_ptr(ptr, cpu) ({ (void)(cpu); VERIFY_PERCPU_PTR(ptr); })
以上get_cpu_ptr和put_cpu_ptr是在有抢占的情况下,需要关闭抢占使用。 而per_cpu_ptr(ptr, cpu)是根据per cpu变量的地址和cpu number,返回指定CPU number上该per cpu变量的地址。
原文地址:https://cloud.tencent.com/developer/article/1995966(版本归原作者所有,侵权删除)
相关推荐
- Rocky Linux 9 系统下安装Nginx(linux怎么安装nginx)
-
RockyLinux9系统下安装Nginx大家好,我是星哥,Nginx凭借其高性能、低资源消耗以及优秀的反向代理能力,已成为Web服务部署的主流选择之一。本文将带你在RockyLinu...
- 你的网站正在裸奔!多个被99%开发者忽略的Nginx配置漏洞
-
"您的服务器已被攻陷,数据库正在导出"——当这条告警弹出时,老王正在茶水间泡咖啡。从黑客发起攻击到拿到核心数据,总共耗时4.7秒,比他点的奶茶送到还快。更讽刺的是,这个价值百万的漏洞并...
- Rocky Linux Nginx 自动更新免费 SSL,全流程实战
-
Let’sEncrypt是由非营利性组织InternetSecurityResearchGroup(ISRG)推动的免费、自动化、开源的证书颁发机构(CA)。它采用ACME协议,让...
- 充分利用站长平台的改版工具降低更换域名的影响
-
boke112导航在前天进行了更换站名和域名,大家都知道更换域名后会带来严重的后果,几乎可以说一朝回到解放前。我也一直都以为只能等搜索引擎慢慢恢复,没想到百度站长平台已经有了这么好的改版工具让我们尽可...
- 别踩雷!外贸人选域名这事,真能决定你独立站是起飞还是扑街
-
有些事,看着不大,结果却能直接决定你是不是要走弯路。域名这事,就是其中一个。说句实话,我见太多外贸人,建站那一刻啥都不想,就随手敲了个域名,“反正能访问就行嘛”。可半年后一看——谷歌排名上不去、客户不...
- 站内SEO优化需要重视这16点才能提升网站排名!
-
1.H标签网站单个页面的H标签尽量控制在一定数量,常见的用法是h1标签1次,h2标签2次,h3标签用3词,也有很大部分的网站不用h1标签直接使用h2标签,这种情况常见于门户站、新闻站等。2.Alt描述...
- Nginx反向代理之proxy_redirect指令
-
proxy_redirect该指令是用来重置头信息中的"Location"和"Refresh"的值。语法:proxy_redirectredirectreplace...
- 2023近期前端面试遇到的题(前端初级)
-
以下面试题不分先后顺序1、vue实现双向数据绑定原理是什么?八股文不想写了自己百度吧...2、v-model语法糖是怎么实现的3、Hash和history有什么区别Hash模式1、定义hash模式是...
- Nginx报404错误的详细解决方法(nginx报错401)
-
最近打开网站看看结果提示:nginx404notfound,所以这篇文章主要给大家介绍了关于Nginx报404错误的简单解决方法,文中通过图文介绍的非常详细,需要的朋友可以参考下宝塔服务器面板,...
- 基于windows的nginx基础使用(windows nginx常用命令)
-
Nginx是一个高性能的HTTP和反向代理服务器,也是一个IMAP/POP3/SMTP服务。易用性强,学习成本低,可维护性高。传送门。欢迎使用 下载nginx/Windows-1.6.3的版...
- 网站为什么要改版,改版网站需要注意什么
-
在平时接触网站项目的时候,会遇到一些客户说,我想要改版网站,网站页面风格需要改变,其实这个不仅是设计页面,前端接入,后台开发代码也要重写,相当于就是重新建站,做个新的设计风格的网站。网站为什么要改版?...
- nginx 代理转发 http https 基本用法
-
nginx配置http最简单的用法;server{#监听的端口listen80;#绑定的域名,多个用空格分开即可。server_nameprvt.c...
- 【Docker系列】最好用的反向代理 ——Nginx Proxy Manager
-
1.介绍访问VPS或者网站服务器需要域名+端口号,之所以日常使用不显示是因为端口80(http访问端口),端口443(https访问端口),会自动加上该默认端口。NginxProxyManag...
- 别小看301重定向,玩明白了SEO真能快人一步
-
在做SEO的路上,有些“老套路”看着平平无奇,但用对了真能让你排名超车。今天想跟你聊的就是这么一个老牌选手——301重定向。很多人一听到“301”就头疼,要么觉得是服务器的事,太技术;要么觉得用处不大...
- 解析:网站降权后是否可以启动新域名
-
站长们都有过网站降权的经历,很多人会选择重新做站,抛弃老域名,觉得降权就是被百度打入谷底、无法翻身。张玉淦博客之前也被降权,排除优化过度,原因很简单,修改了标题,添加“福建”两个字,结果被搜索引擎盯上...
- 一周热门
- 最近发表
-
- Rocky Linux 9 系统下安装Nginx(linux怎么安装nginx)
- 你的网站正在裸奔!多个被99%开发者忽略的Nginx配置漏洞
- Rocky Linux Nginx 自动更新免费 SSL,全流程实战
- 充分利用站长平台的改版工具降低更换域名的影响
- 别踩雷!外贸人选域名这事,真能决定你独立站是起飞还是扑街
- 站内SEO优化需要重视这16点才能提升网站排名!
- Nginx反向代理之proxy_redirect指令
- 2023近期前端面试遇到的题(前端初级)
- Nginx报404错误的详细解决方法(nginx报错401)
- 基于windows的nginx基础使用(windows nginx常用命令)
- 标签列表
-
- linux 远程 (37)
- u盘 linux (32)
- linux 登录 (34)
- linux 路径 (33)
- linux 文件命令 (35)
- linux 是什么 (35)
- linux 界面 (34)
- 查看文件 linux (35)
- linux 语言 (33)
- linux代码 (32)
- linux 查看命令 (33)
- 关闭linux (34)
- root linux (33)
- 删除文件 linux (35)
- linux 主机 (34)
- linux与 (33)
- linux 函数 (35)
- linux .ssh (35)
- cpu linux (35)
- linux 防火墙 (33)
- linux 镜像 (34)
- linux ip地址 (34)
- linux 用户查看 (33)
- nginx配置 解析 (37)
- nginx 频率限制 (34)