0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Linux内核中的jump label原理与逻辑及运行过程

Linux阅码场 来源:CSDN技术社区 作者:dog250 2021-03-25 14:02 次阅读

jump label机制进入Linux内核已经很多很多年了,它的目的是 消除分支。 为了达到这个目的,jump label的手段是 修改分支处的代码。

~把代码当做数据,代码和数据在冯诺伊曼计算机中得到了统一~

本质上,jump label作用于下面的逻辑:

e0bbcc26-8cdd-11eb-8b86-12bb97331649.png

静态拆分成了下面的两个逻辑,其一是:

e0e058fc-8cdd-11eb-8b86-12bb97331649.png

或者,其二是:

e12ea246-8cdd-11eb-8b86-12bb97331649.png

但二者不能同时共存。显然,这破坏了通用性和灵活性,带来了高效!

这相当于一个硬熔断,具体详情参见:

本文来一点可以看得见的东西,演示一下真实的jump label & static key。

先看下面的C代码:

e1519dc8-8cdd-11eb-8b86-12bb97331649.png

很简单的代码,也很正确。然而, 如果main函数是一个高频调用的函数,并且在E1,E2是不随着代码逻辑而发生变化,仅仅参数设定的情况下, 那么if语句尽量消除以消除不必要的分支预测,而这正是jump label的用武之地!

我们下面用jump label机制来重写上面的代码,请看:

e18ee192-8cdd-11eb-8b86-12bb97331649.png

e1e3bc44-8cdd-11eb-8b86-12bb97331649.png

e22b2692-8cdd-11eb-8b86-12bb97331649.png

e26cc53e-8cdd-11eb-8b86-12bb97331649.png

e2a16848-8cdd-11eb-8b86-12bb97331649.png

定义JUMP_LABEL宏编译之,看看效果:

e2c8bb50-8cdd-11eb-8b86-12bb97331649.png

如何做到的呢?static_branch_true内联函数是如何判断true or false的呢?

事实上,jump label逻辑修改了代码段,取消了条件判断!这一切都是在update_branch中发生的。我们看下update_branch调用之前,main函数的汇编码:

e31df26e-8cdd-11eb-8b86-12bb97331649.png

在执行了update_branch之后,main函数发生了变化:

e338ed80-8cdd-11eb-8b86-12bb97331649.png

e39d2124-8cdd-11eb-8b86-12bb97331649.png

看样子就是这么回事!

之所以这件事可以发生得如此简单,多亏了一个新的section,即__jump_table,我们通过objdump看看__jump_table的内容:

e3e7c74c-8cdd-11eb-8b86-12bb97331649.png

通过jump_label_demo.c的struct entry结构体,我们直到这个section中包含了多个3元组,包含3个字段:

需要修改的代码地址。

需要jmp到的代码地址。

匹配健。

我们看67064000 00000000按照小端就是0x400667,它就是需要修改的代码地址,而6e064000 00000000按照小端则是0x40066e:

e41fa0e0-8cdd-11eb-8b86-12bb97331649.png

看来,这个__jump_table的item会将jmpq 40066c修改为jmpq 40066e,从而实现了 永久静态分支。

最后,__jump_table的内容就是在每一个内联的static_branch_true函数中被填充的,该参数的参数是一个key,它指示了branch entry三元组中的最后一个字段。

static_branch_true函数的内联非常重要,它实现了将branch entry三元组数据直接插入到__jump_table section,而不是共享同一个函数体。

总之,如果你看代码还是觉得别扭,手敲一遍我上面的示例程序,就理解了,内核里面的也就这么回事,总结一句话:

依靠运行时修改代码而不是依靠状态数据来控制执行流。

我不知道这对于所谓的 通用计算机程序设计 是不是反其道而行之,但在效果上,它确实是一匹好马。不禁感叹, 硬编码读起来是丑陋的,但执行起来却是高效的!

灵活性换高效率,得不偿失,我是这样以为。jump label的本质在于, 将同时刻存在的一套代码沿着时间线在可预期的固定时间点上分割成逻辑相反的两套代码。

硬件性能的提升将会证明jump label就是个笑话。

说两句好话,Linux内核参数,sysctl变量基本上就可以通过jump label来运作,从而替代if判断。

原文链接:https://blog.csdn.net/dog250/article/details/106715700
编辑:lyn

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 数据
    +关注

    关注

    8

    文章

    6511

    浏览量

    87600
  • 逻辑
    +关注

    关注

    2

    文章

    818

    浏览量

    29293
  • 代码
    +关注

    关注

    30

    文章

    4555

    浏览量

    66767
  • LINUX内核
    +关注

    关注

    1

    文章

    311

    浏览量

    21388

原文标题:Linux内核jump label与static key的原理与示例

文章出处:【微信号:LinuxDev,微信公众号:Linux阅码场】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    Linux内核中信号的传递过程

    前面我们已经介绍了内核注意到信号的到来,调用相关函数更新进程描述符以便进程接收处理信号。但是,如果目标进程此时没有运行内核则推迟传递信号。现在,我们看看内核如何处理进程挂起的信号。
    的头像 发表于 01-17 09:51 461次阅读
    <b class='flag-5'>Linux</b><b class='flag-5'>内核</b>中信号的传递<b class='flag-5'>过程</b>

    Linux内核自解压过程分析

    uboot完成系统引导以后,执行环境变量bootm中的命令;即,将Linux内核调入内存中并调用do_bootm函数启动内核,跳转至kernel的起始位置。
    的头像 发表于 12-08 14:00 400次阅读
    <b class='flag-5'>Linux</b><b class='flag-5'>内核</b>自解压<b class='flag-5'>过程</b>分析

    Linux内核UDP收包为什么效率低

    现在很多人都在诟病Linux内核协议栈收包效率低,不管他们是真的懂还是一点都不懂只是听别人说的,反正就是在一味地怼Linux内核协议栈,他们的武器貌似只有DPDK。 但是,即便
    的头像 发表于 11-13 10:38 238次阅读
    <b class='flag-5'>Linux</b><b class='flag-5'>内核</b>UDP收包为什么效率低

    如何优化Linux内核UDP收包效率低

    很多人都在诟病Linux内核协议栈收包效率低,不管他们是真的懂还是一点都不懂只是听别人说的,反正就是在一味地怼Linux内核协议栈,他们的武器貌似只有DPDK。 但是,
    的头像 发表于 11-10 10:51 279次阅读
    如何优化<b class='flag-5'>Linux</b><b class='flag-5'>内核</b>UDP收包效率低

    linux内核源代码详解

     在安装好的Linux系统中,内核的源代码位于/ust/src/linux.如果是从GNU网站下载的Linux内核的tar文件,则展开以后在
    发表于 09-06 17:01 2次下载

    Linux内核如何使用结构体和函数指针?

    我将结合具体的Linux内核驱动框架代码来展示Linux内核如何使用结构体和函数指针。
    的头像 发表于 09-06 14:17 564次阅读
    <b class='flag-5'>Linux</b><b class='flag-5'>内核</b>如何使用结构体和函数指针?

    Linux内核的编译主要过程

    Linux内核的编译主要过程: 配置、编译、安装 。
    发表于 08-08 16:02 508次阅读
    <b class='flag-5'>Linux</b><b class='flag-5'>内核</b>的编译主要<b class='flag-5'>过程</b>

    万千设备,linux内核如何知道?

    linux内核设备的注册由device_register()函数完成,这个函数是linux设备驱动模型的核心函数
    的头像 发表于 07-12 08:52 502次阅读
    万千设备,<b class='flag-5'>linux</b><b class='flag-5'>内核</b>如何知道?

    linux内核线程就这样诞生了么?

    线程是操作系统的重要组成部件之一,linux内核中,内核线程是如何创建的,在内核启动过程中,诞生了哪些支撑整个系统运转的线程,本文将带着这个
    的头像 发表于 07-10 10:45 465次阅读
    <b class='flag-5'>linux</b><b class='flag-5'>内核</b>线程就这样诞生了么?

    Linux内核的作用

    Linux操作系统是当今世界上最为广泛使用的开源操作系统之一,内核则是一个操作系统的核心和灵魂所在。对于一名Linux驱动开发者来说,了解Linux
    发表于 07-06 11:46 1216次阅读
    <b class='flag-5'>Linux</b><b class='flag-5'>内核</b>的作用

    Linux内核内存泄漏怎么办

    Linux内核开发中,Kmemleak是一种用于检测内核中内存泄漏的工具。
    发表于 07-04 11:04 591次阅读

    Linux内核的编译和运行

    想让Linux内核代码跑起来,得先搭建编译和运行代码的环境。
    发表于 06-23 11:56 356次阅读
    <b class='flag-5'>Linux</b><b class='flag-5'>内核</b>的编译和<b class='flag-5'>运行</b>

    如何编译Linux内核rpm包

    进入github官网,搜索linux,使用git下载最新版本,或者其它版本的内核代码。
    发表于 06-07 16:24 793次阅读
    如何编译<b class='flag-5'>Linux</b><b class='flag-5'>内核</b>rpm包

    Linux内核模块参数传递与sysfs文件系统

    Linux应用开发中,为使应用程序更加灵活地执行用户的预期功能,我们有时候会通过命令行传递一些参数到main函数中,使得代码逻辑可以依据参数执行不同的任务。同样,Linux内核也提供了
    发表于 06-07 16:23 1350次阅读

    Linux内核中常用的C语言技巧有哪些

    Linux内核采用的是GCC编译器,GCC编译器除了支持ANSI C,还支持GNU C。在Linux内核中,许多地方都使用了GNU C语言的扩展特性,如typeof、__attribu
    的头像 发表于 05-12 14:45 402次阅读