linux学习笔记-unlikely

本文介绍了GCC内建函数__builtin_expect()的使用方法及其在Linux内核代码中的应用实例。通过likely()与unlikely()宏定义,程序员可以指导编译器进行更高效的分支预测,从而优化程序性能。

likely,unlikely宏与GCC内建函数__builtin_expect()
先罗嗦几句
最近在读linux 2.6 内核,虽然以前已经看了很多相关的知识,>也看了2,3遍,但读2.6内核仍然感到很吃力。面对2.6如此庞大的内核,信心真的不是很足,而且好像 也没有很好的、有帮助的论坛来一起探讨,哎!现在正在边看>,边看最新的内核,自>出版以来,内核已经有了很多的变化,好难读啊!如果这样读 下去算不算“皓首穷经”呢,不得而知了!
言归正传
在读linux/kernel/fork.c的时候遇到了unlikely宏定义,一路追踪,最后找到了GCC内建函数__builtin_expect(),查阅GCC手册,发现其定义如下:
long __builtin_expect (long exp, long c)                                  [Built-in Function]
You may use __builtin_expect to provide the compiler with branch prediction
information. In general, you should prefer to use actual profile feedback for this
(‘-fprofile-arcs’), as programmers are notoriously bad at predicting how their
programs actually perform. However, there are applications in which this data is
hard to collect.
The return value is the value of exp, which should be an integral expression. The
value of c must be a compile-time constant. The semantics of the built-in are that it
is expected that exp == c. For example:
if (__builtin_expect (x, 0))
foo ();
would indicate that we do not expect to call foo, since we expect x to be zero. Since
you are limited to integral expressions for exp, you should use constructions such as
if (__builtin_expect (ptr != NULL, 1))
error ();
when testing pointer or floating-point values.
大致是说,由于大部分程序员在分支预测方面做得很糟糕,所以GCC提供了这个内建函数来帮助程序员处理分支预测,优化程序。其第一个参数exp为一个整型 表达式,这个内建函数的返回值也是这个exp,而c为一个编译期常量,这个函数的语义是:你期望exp表达式的值等于常量c,从而GCC为你优化程序,将 符合这个条件的分支放在合适的地方。
因为这个程序只提供了整型表达式,所以如果你要优化其他类型的表达式,可以采用指针的形式。
unlikely的定义如下:
#define unlikely(x)   __builtin_expect(!!(x), 0)
也就是说我们期望表达式x的值为0,从而如果我们用
…….
if(unlikely(x)){
bar();
}
来测试条件的话,我们就不期望bar()函数执行,所以该宏的名字用unlikely也就是不太可能来表示。
likely宏与次类似.
说到底__builtin_expect函数就是为了优化可能性大的分支程序。

+++++++++++++++++++++

[bob]
其实, 我总结一下上面的话的意思:
if() 语句你照用, 跟以前一样, 只是 如果你觉得if()是1 的可能性非常大的时候, 就在表达式的外面加一个likely() , 如果可能性非常小(比如几率非常小),就用unlikely() 包裹上。
看kernel代码的时候, 看到很多这样的例子, 举一个:
if (likely(cpu == this_cpu)) {   //注意likely和unlikely的用法
            //这里用likely说明,cpu=this_cpu的可能性非常大
           
                               if (!(clone_flags & CLONE_VM)) {
                     /*
                     * The VM isn't cloned, so we're in a good position to
                     * do child-runs-first in anticipation of an exec. This
                     * usually avoids a lot of COW overhead.
                     */
                     if (unlikely(!current->array))
                               __activate_task(p, rq);
                     else {
                               p->prio = current->prio;
                               p->normal_prio = current->normal_prio;
                               list_add_tail(&p->run_list, &current->run_list);
                               p->array = current->array;
                               p->array->nr_active++;
                               inc_nr_running(p, rq);
                     }
                     set_need_resched();
            } else
                     /* Run child last */
                     __activate_task(p, rq);
            /*
                * We skip the following code due to cpu == this_cpu
               *
                * task_rq_unlock(rq, &flags);
                * this_rq = task_rq_lock(current, &flags);
                */
            this_rq = rq;
       } else {


---

kernel 代码,有许多看起来多余的东西,其实不多余
kernel里面的代码, 有的时候看起来,很繁琐, 冗余, 但是仔细一看 ,还真的不错。
在代码里面老能看到 BUG_ON() , WARN_ON() 这样的宏 , 类似 我们日常编程里面的断言(assert) 。
---> bug.h
QUOTE:
extern void __WARN_ON(const char *func, const char *file, const int line);
#ifdef CONFIG_BUG
#ifndef HAVE_ARCH_BUG
#define BUG() do { /
       printk("BUG: failure at %s:%d/%s()!/n", __FILE__, __LINE__, __FUNCTION__); /
       panic("BUG!"); /
} while (0)
#endif
#ifndef HAVE_ARCH_BUG_ON
#define BUG_ON(condition) do { if (unlikely((condition)!=0)) BUG(); } while(0)
#endif
#ifndef HAVE_ARCH_WARN_ON
#define WARN_ON(condition) do { /
       if (unlikely((condition)!=0)) /
            __WARN_ON(__FUNCTION__, __FILE__, __LINE__); /
} while (0)
#endif
#else /* !CONFIG_BUG */
#ifndef HAVE_ARCH_BUG
#define BUG()
#endif
#ifndef HAVE_ARCH_BUG_ON
#define BUG_ON(condition) do { if (condition) ; } while(0)
#endif
#ifndef HAVE_ARCH_WARN_ON
#define WARN_ON(condition) do { if (condition) ; } while(0)
#endif
#endif
kernel 别的代码里面用到例子:
       BUG_ON(unlikely(atomic_read(&(kioctx)->users)
[Copy to clipboard]

[ - ]
CODE:
#include
#include
#define likely(x)        __builtin_expect(!!(x), 1)
#define unlikely(x)        __builtin_expect(!!(x), 0)
#define BUG_ON(condition) do /
       { if (unlikely((condition)!=0)) /
            printf("BUG: failure at %s:%d/%s()!/n", __FILE__, __LINE__, __FUNCTION__); /
       } while(0)
int main(void)
{
       int i = 7;
       BUG_ON(i!=10);
       return 0;
}
输出如下:
BUG: failure at warn.c:18/main()!

原文网址:http://hi.baidu.com/zkheartboy/blog/item/92cc373f9b90dfe955e72315.html

arm-ca53-linux-gnueabihf-ld: drivers/mtd/spiflash/spinand.o: in function `spinand_probe': spinand.c:(.text.unlikely+0x4e4): undefined reference to `nvt_disable_sram_shutdown' arm-ca53-linux-gnueabihf-ld: drivers/net/ethernet/novatek/DWC_ETH_QOS_drv.o: in function `DWC_ETH_QOS_drv_resume': DWC_ETH_QOS_drv.c:(.text+0x2920): undefined reference to `nvt_get_suspend_mode' arm-ca53-linux-gnueabihf-ld: drivers/net/ethernet/novatek/DWC_ETH_QOS_drv.o: in function `DWC_ETH_QOS_drv_suspend': DWC_ETH_QOS_drv.c:(.text+0x2e0c): undefined reference to `nvt_get_suspend_mode' arm-ca53-linux-gnueabihf-ld: drivers/net/ethernet/novatek/DWC_ETH_QOS_platform.o: in function `nvt_eth_env_probe': DWC_ETH_QOS_platform.c:(.text+0xf8): undefined reference to `nvt_pinmux_capture' arm-ca53-linux-gnueabihf-ld: DWC_ETH_QOS_platform.c:(.text+0x69c): undefined reference to `nvt_pinmux_update' arm-ca53-linux-gnueabihf-ld: drivers/net/ethernet/novatek/DWC_ETH_QOS_platform.o: in function `set_pad_driving': DWC_ETH_QOS_platform.c:(.text.unlikely+0x1c): undefined reference to `pad_set_drivingsink' arm-ca53-linux-gnueabihf-ld: DWC_ETH_QOS_platform.c:(.text.unlikely+0x28): undefined reference to `pad_set_drivingsink' arm-ca53-linux-gnueabihf-ld: DWC_ETH_QOS_platform.c:(.text.unlikely+0x34): undefined reference to `pad_set_drivingsink' arm-ca53-linux-gnueabihf-ld: DWC_ETH_QOS_platform.c:(.text.unlikely+0x40): undefined reference to `pad_set_drivingsink' arm-ca53-linux-gnueabihf-ld: DWC_ETH_QOS_platform.c:(.text.unlikely+0x4c): undefined reference to `pad_set_drivingsink' arm-ca53-linux-gnueabihf-ld: drivers/net/ethernet/novatek/DWC_ETH_QOS_platform.o:DWC_ETH_QOS_platform.c:(.text.unlikely+0x68): more undefined references to `pad_set_drivingsink' follow 这个怎么解决
最新发布
01-01
你遇到的错误是典型的 **链接阶段未定义引用(undefined reference)** 问题: ```bash undefined reference to `nvt_disable_sram_shutdown' undefined reference to `nvt_get_suspend_mode' undefined reference to `nvt_pinmux_capture' undefined reference to `nvt_pinmux_update' undefined reference to `pad_set_drivingsink' ``` 这些函数你在代码里调用了,但链接器找不到它们的实现 → 编译失败。 --- ## ✅ 根本原因 > 这些函数属于 **Novatek 平台底层驱动或 SoC 公共库**,你只包含了头文件声明,但没有链接对应的 `.o` 或 `.a` 实现文件。 --- ## 🔍 错误分类分析 | 函数 | 所属模块 | 可能位置 | |------|----------|--------| | `nvt_disable_sram_shutdown` | 电源管理 / SRAM 控制 | `drivers/soc/nvt/...` | | `nvt_get_suspend_mode` | 挂起模式检测 | `drivers/power/suspend.c` 或平台代码 | | `nvt_pinmux_capture`, `nvt_pinmux_update` | 引脚复用控制 | `drivers/pinctrl/novatek/...` | | `pad_set_drivingsink` | PAD 驱动强度设置 | `drivers/pinctrl/novatek/pad.o` | 👉 它们都属于 **NVT 自有平台支持代码**,必须正确编译进内核或作为依赖被链接。 --- ## ✅ 解决方案 ### ✅ 方案一:确保相关平台驱动已启用并编入内核 #### 步骤 1:检查 Kconfig 是否启用 Novatek 平台支持 打开你的内核配置: ```bash make menuconfig ``` 确认以下选项已开启(通常为 `y` 或 `m`): ```text Device Drivers ---> [*] Pinctrl support ---> <*> Novatek pinctrl driver <*> Novatek pad control driver SOC Support ---> <*> Support for Novatek NA51055 ``` 如果没有这些选项,说明你需要先添加平台支持。 --- #### 步骤 2:检查 `Makefile` 是否包含对应源码 进入: ``` drivers/pinctrl/novatek/ ``` 查看 `Makefile` 内容是否包含: ```makefile obj-$(CONFIG_PINCTRL_NOVATEK) += pinctrl-nvt.o pinctrl-nvt-objs := pinmux.o pad.o ``` 或者类似结构。 如果 `pad.c`、`pinmux.c` 没有被编译 → 就不会生成 `pad.o` → 导致 `pad_set_drivingsink` 无法链接。 --- ### ✅ 方案二:手动将目标文件加入你的模块链接(临时 workaround) 如果你只是想快速验证功能,可以强制把缺失的目标文件一起链接进去。 修改你的驱动所在目录的 `Makefile`(例如 `drivers/net/ethernet/novatek/Makefile`): ```makefile # 原始 obj-$(CONFIG_DWC_ETH_QOS_NVT) += DWC_ETH_QOS.o DWC_ETH_QOS-objs := \ DWC_ETH_QOS_main.o \ DWC_ETH_QOS_drv.o \ DWC_ETH_QOS_platform.o \ DWC_ETH_QOS_linux.o # 修改后:显式加入依赖的 .o 文件 DWC_ETH_QOS-objs := \ DWC_ETH_QOS_main.o \ DWC_ETH_QOS_drv.o \ DWC_ETH_QOS_platform.o \ DWC_ETH_QOS_linux.o \ ../../pinctrl/novatek/pad.o \ ../../pinctrl/novatek/pinmux.o \ ../../soc/nvt/nvt-power.o \ ../../soc/nvt/nvt-suspend.o ``` 📌 注意路径要根据实际存在 `.c/.o` 文件的位置调整。 ⚠️ 这种方式仅用于调试!正式构建应通过 Kbuild 系统统一管理。 --- ### ✅ 方案三:使用 `EXPORT_SYMBOL()` 确保符号导出 检查 `pad.c` 中是否有: ```c ER pad_set_drivingsink(PAD_DS name, PAD_DRIVINGSINK driving) { // 实现... } EXPORT_SYMBOL(pad_set_drivingsink); ``` 如果没有 `EXPORT_SYMBOL(...)`,即使编译进内核也无法被模块引用! 同样地,其他函数如 `nvt_pinmux_capture` 也需要导出: ```c extern int nvt_pinmux_capture(void); EXPORT_SYMBOL(nvt_pinmux_capture); ``` 👉 如果你是平台开发者,请在对应 `.c` 文件末尾加上: ```c EXPORT_SYMBOL(nvt_disable_sram_shutdown); EXPORT_SYMBOL(nvt_get_suspend_mode); EXPORT_SYMBOL(nvt_pinmux_capture); EXPORT_SYMBOL(nvt_pinmux_update); ``` 然后重新编译整个内核。 --- ### ✅ 方案四:使用 `kallsyms` 动态查找符号(高级,不推荐) 如果你不能修改平台代码且符号未导出,可以用 `kprobes` + `kallsyms_lookup_name()` 动态获取函数地址(需开启 `CONFIG_KALLSYMS`): ```c #include <linux/kallsyms.h> typedef int (*nvt_pinmux_capture_t)(void); static nvt_pinmux_capture_t my_nvt_pinmux_capture; // 初始化时查找 my_nvt_pinmux_capture = (nvt_pinmux_capture_t)kallsyms_lookup_name("nvt_pinmux_capture"); if (!my_nvt_pinmux_capture) { pr_err("Symbol 'nvt_pinmux_capture' not found!\n"); return -ENODEV; } ``` ⚠️ 缺点: - 不安全(符号可能不存在) - 依赖 `CONFIG_KALLSYMS=on` - 不适用于生产环境 --- ## ✅ 排查流程图(建议操作顺序) ```bash 1. grep -r "pad_set_drivingsink" drivers/ → 找到它在哪个 .c 文件中定义 2. 查看该文件是否被 Makefile 编译(生成 .o) 3. 查看该函数是否使用 EXPORT_SYMBOL() 4. make menuconfig 确认相关 CONFIG_XXX 已启用 5. make v=1 查看完整链接命令,确认所有 .o 都参与了链接 6. 若仍失败,临时手动添加 .o 到 DWC_ETH_QOS-objs 中测试 ``` --- ## ✅ 示例修复:添加 `pad.o` 到编译链 假设 `pad.c` 路径是: ``` drivers/pinctrl/novatek/pad.c ``` 其 Makefile 应该有: ```makefile # drivers/pinctrl/novatek/Makefile obj-$(CONFIG_PINCTRL_NOVATEK) += pinctrl-nvt.o pinctrl-nvt-objs := pad.o pinmux.o ``` 并在 `Kconfig` 中启用: ```kconfig config PINCTRL_NOVATEK bool "Novatek Pinctrl Driver" depends on ARCH_NVT || COMPILE_TEST default y if ARCH_NVT ``` 然后你在 `.config` 中要有: ```ini CONFIG_PINCTRL_NOVATEK=y ``` 这样才能确保 `pad_set_drivingsink` 被编译和导出。 --- ## ✅ 总结:你现在应该怎么做? | 步骤 | 操作 | |------|------| | 1 | `grep -r "pad_set_drivingsink" .` 找到定义文件 | | 2 | 确认该文件所在的 `Makefile` 会将其编译成 `.o` | | 3 | 确保函数上有 `EXPORT_SYMBOL(func_name);` | | 4 | 在 `menuconfig` 中启用对应 `CONFIG_XXX` | | 5 | 重新编译整个内核或模块 | | 6 | 若仍不行,临时手动将 `.o` 加入 `DWC_ETH_QOS-objs` 测试 | --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值