Linux Static Key原理与应用
文章目录
- 背景
- 1. static-key的使用方法
- 1.1. static-key定义
- 1.2 初始化
- 1.3 条件判断
- 1.4 修改判断条件
- 2、示例代码
- 参考链接
背景
内核中有很多判断条件在正常情况下的结果都是固定的,除非极其罕见的场景才会改变,通常单个的这种判断的代价很低可以忽略,但是如果这种判断数量巨大且被频繁执行,那就会带来性能损失了。内核的static-key机制就是为了优化这种场景,其优化的结果是:对于大多数情况,对应的判断被优化为一个NOP指令,在非常有场景的时候就变成jump XXX一类的指令,使得对应的代码段得到执行。

1. static-key的使用方法
1.1. static-key定义
static_key 结构体的定义如下:
#ifdef CONFIG_JUMP_LABELstruct static_key {atomic_t enabled;
/** Note:* To make anonymous unions work with old compilers, the static* initialization of them requires brackets. This creates a dependency* on the order of the struct with the initializers. If any fields* are added, STATIC_KEY_INIT_TRUE and STATIC_KEY_INIT_FALSE may need* to be modified.** bit 0 => 1 if key is initially true* 0 if initially false* bit 1 => 1 if points to struct static_key_mod* 0 if points to struct jump_entry*/union {unsigned long type;struct jump_entry *entries;struct static_key_mod *next;};
};#else
struct static_key {atomic_t enabled;
};
#endif /* CONFIG_JUMP_LABEL */
如果没有定义CONFIG_JUMP_LABEL,则static_key 退化成atomic变量。
1.2 初始化
#define DEFINE_STATIC_KEY_TRUE(name) \struct static_key_true name = STATIC_KEY_TRUE_INIT
#define DEFINE_STATIC_KEY_FALSE(name) \struct static_key_false name = STATIC_KEY_FALSE_INIT
#define STATIC_KEY_TRUE_INIT (struct static_key_true) { .key = STATIC_KEY_INIT_TRUE, }
#define STATIC_KEY_FALSE_INIT (struct static_key_false){ .key = STATIC_KEY_INIT_FALSE, }#define STATIC_KEY_INIT_TRUE \{ .enabled = { 1 }, \.entries = (void *)JUMP_TYPE_TRUE }
#define STATIC_KEY_INIT_FALSE \{ .enabled = { 0 }, \.entries = (void *)JUMP_TYPE_FALSE }
false和true的主要区别就是enabled 是否为1.
1.3 条件判断
#ifdef CONFIG_JUMP_LABEL/** Combine the right initial value (type) with the right branch order* to generate the desired result.*** type\branch| likely (1) | unlikely (0)* -----------+-----------------------+------------------* | |* true (1) | ... | ...* | NOP | JMP L* | <br-stmts> | 1: ...* | L: ... |* | |* | | L: <br-stmts>* | | jmp 1b* | |* -----------+-----------------------+------------------* | |* false (0) | ... | ...* | JMP L | NOP* | <br-stmts> | 1: ...* | L: ... |* | |* | | L: <br-stmts>* | | jmp 1b* | |* -----------+-----------------------+------------------** The initial value is encoded in the LSB of static_key::entries,* type: 0 = false, 1 = true.** The branch type is encoded in the LSB of jump_entry::key,* branch: 0 = unlikely, 1 = likely.** This gives the following logic table:** enabled type branch instuction* -----------------------------+-----------* 0 0 0 | NOP* 0 0 1 | JMP* 0 1 0 | NOP* 0 1 1 | JMP** 1 0 0 | JMP* 1 0 1 | NOP* 1 1 0 | JMP* 1 1 1 | NOP** Which gives the following functions:** dynamic: instruction = enabled ^ branch* static: instruction = type ^ branch** See jump_label_type() / jump_label_init_type().*/#define static_branch_likely(x) \
({ \bool branch; \if (__builtin_types_compatible_p(typeof(*x), struct static_key_true)) \branch = !arch_static_branch(&(x)->key, true); \else if (__builtin_types_compatible_p(typeof(*x), struct static_key_false)) \branch = !arch_static_branch_jump(&(x)->key, true); \else \branch = ____wrong_branch_error(); \likely(branch); \
})#define static_branch_unlikely(x) \
({ \bool branch; \if (__builtin_types_compatible_p(typeof(*x), struct static_key_true)) \branch = arch_static_branch_jump(&(x)->key, false); \else if (__builtin_types_compatible_p(typeof(*x), struct static_key_false)) \branch = arch_static_branch(&(x)->key, false); \else \branch = ____wrong_branch_error(); \unlikely(branch); \
})#else /* !CONFIG_JUMP_LABEL */#define static_branch_likely(x) likely(static_key_enabled(&(x)->key))
#define static_branch_unlikely(x) unlikely(static_key_enabled(&(x)->key))#endif /* CONFIG_JUMP_LABEL */
可见同样依赖HAVE_JUMP_LABEL。如果没有定义的话,直接退化成likely和unlikely
static_branch_unlikely 和 static_branch_likely 只是填充指令的方式不同(可以参考上面的代码注释), 当static_key为false时,都会进入else逻辑语句中。
if (static_branch_unlikely((&static_key)))do likely work;
elsedo unlikely work
1.4 修改判断条件
使用static_branch_enable 和 static_branch_disable可以改变static_key 状态
#define static_branch_enable(x) static_key_enable(&(x)->key)
#define static_branch_disable(x) static_key_disable(&(x)->key)
底层是调用static_key_slow_dec, static_key_slow_dec来改变key->enabled计数。
static inline void static_key_enable(struct static_key *key)
{int count = static_key_count(key);WARN_ON_ONCE(count < 0 || count > 1);if (!count)static_key_slow_inc(key);
}
static inline void static_key_disable(struct static_key *key)
{int count = static_key_count(key);WARN_ON_ONCE(count < 0 || count > 1);if (count)static_key_slow_dec(key);
}
static inline void static_key_slow_inc(struct static_key *key)
{STATIC_KEY_CHECK_USE(key);atomic_inc(&key->enabled);
}static inline void static_key_slow_dec(struct static_key *key)
{STATIC_KEY_CHECK_USE(key);atomic_dec(&key->enabled);
}
2、示例代码
下面我们用一段代码来分析static-key对程序分支跳转硬编码的影响。
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/static_key.h>DEFINE_STATIC_KEY_FALSE(key);void func(int a){if (static_branch_unlikely(&key)) { printk("my_module: Feature is enabled\n");} else {printk("my_module: Feature is disabled\n");}
}static int __init my_module_init(void) {pr_info("my_module: Module loaded\n");int a = 1;func(a);static_branch_enable(&key);func(a);return 0;
}static void __exit my_module_exit(void) {pr_info("my_module: Module unloaded\n");
}module_init(my_module_init);
module_exit(my_module_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Sample Kernel Module with Static Key");
func汇编代码如下:
0000000000000000 <func>:0: a9bf7bfd stp x29, x30, [sp, #-16]!4: 910003fd mov x29, sp8: d503201f nopc: 90000000 adrp x0, 0 <func>10: 91000000 add x0, x0, #0x014: 94000000 bl 0 <printk>18: a8c17bfd ldp x29, x30, [sp], #161c: d65f03c0 ret20: 90000000 adrp x0, 0 <func>24: 91000000 add x0, x0, #0x028: 94000000 bl 0 <printk>2c: 17fffffb b 18 <func+0x18>
func中不适用static-key时,汇编代码如下:
void func(int a){if (a) { printk("my_module: Feature is enabled\n");} else {printk("my_module: Feature is disabled\n");}
}0000000000000000 <func>:0: a9bf7bfd stp x29, x30, [sp, #-16]!4: 910003fd mov x29, sp8: 340000a0 cbz w0, 1c <func+0x1c>c: 90000000 adrp x0, 0 <func>10: 91000000 add x0, x0, #0x014: 94000000 bl 0 <printk>18: 14000004 b 28 <func+0x28>1c: 90000000 adrp x0, 0 <func>20: 91000000 add x0, x0, #0x024: 94000000 bl 0 <printk>28: a8c17bfd ldp x29, x30, [sp], #162c: d65f03c0 ret
对比可以发现,在0x8地址处,使用static-key编码在编译时将cbz指令替换为了nop指令,减少了程序运行时对比次数。
参考链接
- Linux内核中的static-key机制
- Linux内核jump label与static key的原理与示例
- static-keys.html | 静态键
- Linux Jump Label/static-key机制详解
相关文章:
Linux Static Key原理与应用
文章目录 背景1. static-key的使用方法1.1. static-key定义1.2 初始化1.3 条件判断1.4 修改判断条件 2、示例代码参考链接 背景 内核中有很多判断条件在正常情况下的结果都是固定的,除非极其罕见的场景才会改变,通常单个的这种判断的代价很低可以忽略&a…...
linux ssh 禁止指定用户通过ssh登录
Linux 禁止用户或 IP通过 SSH 登录 限制用户 SSH 登录 1.只允许指定用户进行登录(白名单): 在 /etc/ssh/sshd_config 配置文件中设置 AllowUsers 选项,(配置完成需要重启 SSHD 服务)格式如下:…...
快速学习Netty
Netty框架探索:助力高效网络编程 一、Netty是个啥?二、“Hello World”服务器端实现(Server)客户端实现(Client)思考🤔 三、Netty的核心组件EventLoopChannelChannelPipelineChannelHandlerByte…...
对类和对象的详细解析
目录 1.类的构成 2.this指针 3.C类里面的静态成员 3.1 类里面的静态成员函数 3.2 类里面的静态成员变量 静态成员变量的特点 共享性 存储位置 生命周期 访问权限 如何初始化? 构造函数 1.类的构成 public修饰的成员在类外可以直接被访问 private和protecte…...
matlab 间接平差法拟合二维圆
目录 一、算法原理二、代码实现三、结果展示本文由CSDN点云侠原创,原文链接。爬虫自重。 一、算法原理 圆的方程为: ( x - x 0 )...
pgzrun 拼图游戏制作过程详解(10)
10. 拼图游戏继续升级——多关卡拼图 初始化列表Photos用来储存拼图文件名,Photo_ID用来统计当下是第几张拼图,Squares储存当下拼图的24张小拼图的文件名,Gird储存当下窗口上显示的24个小拼图及坐标。 Photos["girl_","boy_…...
glog与pugi::xml使用方法
(一)glog的使用:google logging的简称; 1)需要安装,网上一搜一大堆,不在赘述; 2)在cmakelists.txt中,需要链接"-glog",如&a…...
windows下MySQL服务不见,服务无法启动,服务闪退,提示“本地计算机上的MySQL服务启动后停止。某些服务在未由其他服务或程序使用时将自动停止”
文章目录 前情提示1.解决MySQL服务消失2.解决MySQL服务无法启动 前情提示 后台启动MySQL服务出现闪退 或 “本地计算机上的MySQL服务启动后停止。某些服务在未由其他服务或程序使用时将自动停止”,可以参考以下方法。 我的电脑上安装了双版本MySQL,这里…...
剑指offer(C++)-JZ67:把字符串转换成整数atoi(算法-模拟)
作者:翟天保Steven 版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处 题目描述: 写一个函数 StrToInt,实现把字符串转换成整数这个功能。不能使用 atoi 或者其他类似的库函数。…...
嵌入式笔试面试刷题(day15)
文章目录 前言一、Linux中的主设备号和次设备号1.查看方法2.主设备号和次设备号的作用 二、软件IIC和硬件IIC的区别三、变量的声明和定义区别四、static在C和C中的区别五、串口总线空闲时候的电平状态总结 前言 本篇文章继续讲解嵌入式笔试面试刷题,希望大家坚持跟…...
【Docker】Dockerfile构建镜像
一、编写Dockerfile文件 编写镜像需要的运行环境(Linux、java等), Dockerfile文件内容如下: # 使用官方的 Ubuntu 16.04 镜像作为基础镜像 FROM ubuntu:16.04# 更新包列表 RUN apt-get update# 安装所需的软件包 RUN apt-get ins…...
fota升级,可卸载apk也进行更新
首先如题目要求 可卸载apk是通过刷机或恢复出厂设置之后执行脚本安装的 然后fota升级后,在判断是否“是第一次刷机和恢复出厂设置”时候会返回false,就导致脚本没有执行。导致apk升级不成功 所以我们要完成这个就是,确定fota什么时候升级完…...
ASP.NET dotnet 3.5 实验室信息管理系统LIMS源码
技术架构:ASP.NET dotnet 3.5 LIMS作为一个信息管理系统,它有着和ERP、MIS之类管理软件的共性,如它是通过现代管理模式与计算机管理信息系统支持企业或单位合理、系统地管理经营与生产,最大限度地发挥现有设备、资源、人、技术的…...
2023!6招玩转 Appium 自动化测试
Appium是个什么鬼 Appium是一个移动端的自动化框架,可用于测试原生应用,移动网页应用和混合型应用,且是跨平台的。可用于IOS和Android以及firefox的操作系统。原生的应用是指用android或ios的sdk编写的应用,移动网页应用是指网页…...
WireShark抓包分析TCP三次握手过程,TCP报文解析
「作者主页」:士别三日wyx 「作者简介」:CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」:对网络安全感兴趣的小伙伴可以关注专栏《网络安全入门到精通》 使用WireShark工具抓取TCP协议三次握手的数据包&am…...
【C语言】指针和数组笔试题解析
大家好,我是苏貝,本篇博客带大家了解指针和数组笔试题解析,如果你觉得我写的还不错的话,可以给我一个赞👍吗,感谢❤️ 目录 1.前言2.一维数组2.字符数组2.12.22.32.42.52.6 1.前言 本篇文章是讲述在不同数…...
Vue的模板语法(下)
一.事件处理 事件修饰符 Vue通过由点(.)表示的指令后缀来调用修饰符, .stop, .prevent,.capture,.self,.once .stop:阻止事件冒泡。当一个元素触发了事件,并且该元素包含嵌套的父元素时&#…...
Zookeeper客户端——I0Itec-zkClient
dubbo使用了zkClient而不是使用zookeeper本身的客户端与zookeeper进行交互,为什么呢? 先看看zookeeper本身自带的客户端的问题。 1)ZooKeeper的Watcher是一次性的,用过了需要再注册; 2) session的超时后…...
火山引擎 ByteHouse:ClickHouse 如何保证海量数据一致性
背景 ClickHouse是一个开源的OLAP引擎,不仅被全球开发者广泛使用,在字节各个应用场景中也可以看到它的身影。基于高性能、分布式特点,ClickHouse可以满足大规模数据的分析和查询需求,因此字节研发团队以开源ClickHouse为基础&…...
hashmap使用
hashmap作为dao对象存储数据库数据 list是把每一个数据库的字段都映射了,而hashmap则是唯一id:数据库字段作为key hashmap遍历方式 public class Main {//使用迭代器(Iterator)EntrySetpublic static void main(String[] args) {// 创建并赋…...
AI画家助手:OpenClaw+GLM-4.7-Flash自动生成Midjourney提示词并管理作品
AI画家助手:OpenClawGLM-4.7-Flash自动生成Midjourney提示词并管理作品 1. 为什么需要AI画家助手? 去年我开始尝试用Midjourney进行艺术创作时,遇到了两个头疼的问题:一是提示词(prompt)优化需要反复调试…...
COMSOL 多物理场建模:热流固耦合与压缩空气
comsol多物理场: 热流固耦合 压缩空气 应力场 温度场 渗流场在现代工程设计中,多物理场问题越来越常见,尤其是在涉及热、流体、结构等相互作用的复杂系统中。本文将介绍如何利用 COMSOL 多物理场建模工具来解决一个典型的热流固耦合问题——压…...
OpenClaw长任务管理:Qwen3-VL:30B连续执行优化
OpenClaw长任务管理:Qwen3-VL:30B连续执行优化 1. 长任务管理的痛点与挑战 上周我尝试用OpenClaw自动化处理一个复杂的市场分析报告生成任务。这个任务需要连续执行网页搜索、数据提取、图表生成和报告撰写四个步骤,预计耗时约40分钟。然而在第三次运行…...
从语义到轨迹:VLM/VLA如何重塑端到端自动驾驶的推理与执行
1. 视觉语言大模型如何打通自动驾驶的任督二脉 想象一下你坐在副驾驶教新手开车:"前面路口左转,注意右侧电动车,减速让行"——人类教练用语言就能传递复杂驾驶策略。但传统自动驾驶系统像两个语言不通的部门:感知模块输…...
OpenClaw学习助手:百川2-13B驱动的自动化笔记整理系统
OpenClaw学习助手:百川2-13B驱动的自动化笔记整理系统 1. 为什么需要自动化笔记整理 作为一个经常需要阅读大量技术文档和论文的开发者,我发现自己陷入了一个困境:每次下载新的PDF或PPT文件后,要么没时间仔细阅读,要…...
Virtual-Display-Driver终极指南:Windows虚拟显示器驱动完整配置与优化教程
Virtual-Display-Driver终极指南:Windows虚拟显示器驱动完整配置与优化教程 【免费下载链接】Virtual-Display-Driver Add virtual monitors to your windows 10/11 device! Works with VR, OBS, Sunshine, and/or any desktop sharing software. 项目地址: https…...
红海跟风:为何亚马逊上“更好的产品”往往死得最快
在亚马逊这片由算法和心智认知共同统治的战场上,无数跟随者都死于同一种“绝症”:相信只要产品比领导者“更好”,就理应获得成功。 他们投入大量时间优化参数、增加无关紧要的功能、或进行微弱的降价,却将最宝贵的“时机”和“心…...
Python数据处理:Anything to RealCharacters 2.5D引擎结果分析与可视化
Python数据处理:Anything to RealCharacters 2.5D引擎结果分析与可视化 1. 引言 作为一名数据分析师,你可能经常遇到这样的场景:团队使用Anything to RealCharacters 2.5D引擎生成了大量转换结果,但如何系统性地分析这些结果的质…...
python-数字中药材资源共享平台vue
目录需求分析与架构设计前端实现(Vue 3 TypeScript)后端实现(Python)数据库设计开发与测试流程部署方案关键代码示例(FastAPI Vue)注意事项项目技术支持源码获取详细视频演示 :文章底部获取博…...
TradingAgents-CN:多智能体协作的金融交易AI框架深度解析
TradingAgents-CN:多智能体协作的金融交易AI框架深度解析 【免费下载链接】TradingAgents-CN 基于多智能体LLM的中文金融交易框架 - TradingAgents中文增强版 项目地址: https://gitcode.com/GitHub_Trending/tr/TradingAgents-CN 1 技术原理:多智…...
