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) {// 创建并赋…...
IDEA运行Tomcat出现乱码问题解决汇总
最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…...
(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...
国防科技大学计算机基础课程笔记02信息编码
1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...
Cesium1.95中高性能加载1500个点
一、基本方式: 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...
Qt Widget类解析与代码注释
#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码,写上注释 当然可以!这段代码是 Qt …...
python如何将word的doc另存为docx
将 DOCX 文件另存为 DOCX 格式(Python 实现) 在 Python 中,你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是,.doc 是旧的 Word 格式,而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...
【git】把本地更改提交远程新分支feature_g
创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...
JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案
JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停 1. 安全点(Safepoint)阻塞 现象:JVM暂停但无GC日志,日志显示No GCs detected。原因:JVM等待所有线程进入安全点(如…...
蓝桥杯 冶炼金属
原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...
人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...
