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_unlikelystatic_branch_likely 只是填充指令的方式不同(可以参考上面的代码注释), 当static_key为false时,都会进入else逻辑语句中。

if (static_branch_unlikely((&static_key)))do likely work;
elsedo unlikely work

1.4 修改判断条件

使用static_branch_enablestatic_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指令,减少了程序运行时对比次数。

参考链接

  1. Linux内核中的static-key机制
  2. Linux内核jump label与static key的原理与示例
  3. static-keys.html | 静态键
  4. Linux Jump Label/static-key机制详解

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/136792.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python3 语法简明教程

目录 0 前言1 输出语句、注释1.1 输出语句1.2 单行、多行注释 2 变量与对象、输入语句2.1 变量与对象2.2 数字类型和运算符2.3 输入语句 3 条件控制、循环语句3.1 条件控制3.2 循环语句3.2.1 while...else3.2.2 for...else 4 字典5 元组、列表5.1 元组5.2 列表 6 集合、推导式6…

C语言——贪吃蛇小游戏

目录 一、ncurse 1.1 为什么需要用ncurse&#xff1a; 1.2 ncurse的输入输出&#xff1a; 1.2.1 如何使用ncurse&#xff1a; 1.2.2 编译ncurse的程序&#xff1a; 1.2.3 测试输入一个按键ncurse的响应速度&#xff1a; 1.3 ncurse上下左右键获取&#xff1a; 1.3.1 如…

【IntelliJ IDEA】cmd和idea Terminal查看java版本不一致

问题描述 原来win10电脑上安装的是jdk8的版本&#xff0c;因某些原因&#xff0c;现在想换成jdk7的版本&#xff0c;修改环境变量后&#xff0c;在cmd中执行 [java -version]命令&#xff0c;显示的是7的版本。 但在idea的Terminal中执行&#xff0c;确实显示8的版本。 原因分…

基于深度强化学习的四旋翼无人机航线跟随

源自&#xff1a;指挥与控制学报 作者&#xff1a;杨志鹏 李波 甘志刚 梁诗阳 摘 要 针对无人机在空中执行航线跟随任务时无法对未知环境作出合理应对措施等问题, 提出了一种基于深度强化学习的四 旋翼无人机航线跟随方法. 通过无人机受力分析、欧拉角变换建立四旋翼无人…

2023.9.18 网络层 IP 协议详解

目录 IP协议 IPv4 32位 源IP 地址 / 32位 目的IP 地址 IP 地址管理 特殊 IP 路由选择 IP协议 IPv4 32位 源IP 地址 / 32位 目的IP 地址 基本知识&#xff1a; 在 IP 报头中一般表示为 32位 二进制整数日常生活中的 IP 一般将 32位 二进制整数&#xff0c;也就是 4字节 的二…

2023/9/19 -- C++/QT

作业 1> 登录框实现注册功能&#xff0c;将注册的结果放入文件中&#xff08;君子作业&#xff09; 2> 完成文本编辑器的保存工作 void Widget::on_saveBtn_clicked() {QString fileName QFileDialog::getSaveFileName(this,"另存为","./","…

036:vue导出页面生成pdf文件

第036个 查看专栏目录: VUE ------ element UI 专栏目标 在vue和element UI联合技术栈的操控下&#xff0c;本专栏提供行之有效的源代码示例和信息点介绍&#xff0c;做到灵活运用。 &#xff08;1&#xff09;提供vue2的一些基本操作&#xff1a;安装、引用&#xff0c;模板使…

元数据管理平台Datahub0.10.5版本安装部署与导入各种元数据手册

官网文档连接 DataHub Quickstart Guide | DataHub (datahubproject.io) 本文所选择的Python的版本为3.8.16&#xff0c;Docker版本为20.10.0&#xff0c;Datahub为0.10.5版本 python必须为3.7以上的版本。0.10.5不支持以下的版本 如果要使用web上的 添加数据源 直接调用的…

中国提出FastSAM:在RTX3090上提升了ViT-H E(32×32) 50倍速度

文章目录 1. Abstract2. 背景介绍3. 框架详情 (Methodology)3.1 Overview3.2 All-instance Segmentation3.3 Prompt-guided Selection4. Experiments4.1 Run-time Efficiency Evaluation4.2 Zero-Shot Edge Detection4.2.1 BSDS5004.2.2 Sobel filtering4.2.3 NMS4.3 Zero-Shot…

Redis 列表操作实战(全)

目录 LINDEX 获取指定下标元素 LSET 指定下标添加元素 LPUSH 将元素插入列表头 LPUSHX RPUSH 将元素插入列表尾 RPUSHX LINSERT 将元素插入列表某位置之前 LLEN 列表长度 LPOP 取列表头元素 RPOP 取列表尾元素 BLPOP 阻塞式取列表头元素 BRPOP 阻塞式取列表尾元素…

视频监控系统/视频汇聚平台EasyCVR平台页面展示优化

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安…

企望制造ERP存在远程命令执行漏洞 附POC

文章目录 企望制造ERP存在远程命令执行漏洞 附POC1. 企望制造ERP简介2.漏洞描述3.影响版本4.fofa查询语句5.漏洞复现6.POC&EXP7.整改意见8.往期回顾 企望制造ERP存在远程命令执行漏洞 附POC 免责声明&#xff1a;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播…

C# OpenCvSharp 图片模糊检测(拉普拉斯算子)

效果 项目 代码 using OpenCvSharp; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Windows.Forms.VisualStyl…

python+django学习资料在线分享系统vue

本站是一个B/S模式系统&#xff0c;采用vue框架作为开发技术&#xff0c;MYSQL数据库设计开发&#xff0c;充分保证系统的稳定性。系统具有界面清晰、操作简单&#xff0c;功能齐全的特点&#xff0c;使得校园资料分享平台管理工作系统化、规范化。技术栈 后端&#xff1a;pyth…

零基础学前端(四)1. 重点讲解 CSS:盒子模型、样式选择器

1. 该篇适用于从零基础学习前端的小白 2. 初学者不懂代码得含义也要坚持模仿逐行敲代码&#xff0c;以身体感悟带动头脑去理解新知识 3. 初学者切忌&#xff0c;不要眼花缭乱&#xff0c;不要四处找其它文档&#xff0c;要坚定一个教授者的方式&#xff0c;将其学通透&#xff…

RK3568平台开发系列讲解(驱动篇)RK3568 I2C总线介绍

🚀返回专栏总目录 文章目录 一、I2C 简介1.1、起始位1.2、停止位1.3、数据传输1.4、应答信号1.5、I2C 写时序1.6、I2C 读时序1.7、I2C 多字节读写时序二、RK3568 I2C 总线介绍沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇我们将讲解RK3568 I2C总线特性。 一、…

使用 K 均值聚类进行颜色分割

介绍 颜色分割是计算机视觉中使用的一种技术,用于根据颜色识别和区分图像中的不同对象或区域。聚类算法可以自动将相似的颜色分组在一起,而不需要为每种颜色指定阈值。当处理具有大范围颜色的图像时,或者当事先不知道确切的阈值时,这非常有用。 在本教程中,我们将探讨如何…

Zipping

Zipping 信息收集端口扫描目录扫描webbanner信息收集 漏洞利用空字节绕过---->失败sqlI-preg_match bypass反弹shell 稳定维持 提权-共享库漏洞 参考&#xff1a;https://rouvin.gitbook.io/ibreakstuff/writeups/htb-season-2/zipping#sudo-privileges-greater-than-stock-…

【ComfyUI】RuntimeError: CUDA error: operation not supported

文章目录 前言解决办法方式一&#xff1a;黑名单策略方式二&#xff1a;启动时添加--disable-cuda-malloc 前言 最近好不容易&#xff0c;安装好ComfyUI后&#xff0c;启动也OK&#xff0c;点击生成图片时&#xff0c;报错了 got prompt model_type EPS adm 0 making attenti…

Postman应用——Collection、Folder和Request

文章目录 Collection新建CollectionCollection重命名保存Request到Collection在Collection下创建Request删除Collection Folder新建FolderFolder重命名保存Request到Folder在Folder下创建Request在Folder下创建Folder删除Folder Request创建临时RequestRequest重命名删除Reques…