Likely and Unlikely in linux

本文详细介绍了Linux编程中likely和unlikely宏的作用及其内部实现原理。这两种宏用于指导编译器进行分支预测,通过调整指令顺序来提高程序运行效率。

linux中判断语句经常会看到likely和unlikely,例如:

if(likely(value)){

}

else{

}

简单从表面上看if(likely(value)) == if(value),if(unlikely(value)) == if(value)。

也就是likely和unlikely是一样的,但是实际上执行是不同的,加likely的意识是value的值为真的可能

性更大一些,那么执行if的机会大,而unlikely表示value的值为假的可能性大一些,执行else机会大一些。

加上这种修饰,编译成二进制代码时likely使得if后面的执行语句紧跟着前面的程序,unlikely使得else后

面的语句紧跟着前面的程序,这样就会被cache预读取,增加程序的执行速度,likely和unlikely的实现在

include/linux/compiler.h中:

      9 #if __GNUC__ == 2 && __GNUC_MINOR__ < 96
     10 #define __builtin_expect(x, expected_value) (x)
     11 #endif
     12 
     13 #define likely(x)   __builtin_expect((x),1)
     14 #define unlikely(x) __builtin_expect((x),0)

__builtin_expect是gcc的一个预处理命令,其解释如下:

long __builtin_expect (long exp, long c)
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.

### Linux Kernel 中 `cmpxchg` 函数的用途与实现 在 Linux 内核中,`cmpxchg` 是一种原子操作函数,通常用于同步访问共享资源。它基于硬件支持的比较并交换指令来实现,能够在一个单一的操作中完成以下功能: 1. 将目标内存位置的内容与预期值进行比较。 2. 如果两者相等,则将目标内存位置更新为目标值;如果不相等,则不执行任何更改。 这种机制广泛应用于多线程环境下的锁管理、计数器控制以及其他需要高并发性能的任务场景[^3]。 #### 实现细节 以下是 `cmpxchg` 的典型定义形式及其工作原理说明: ```c static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size) { switch (size) { case 1: return __sync_val_compare_and_swap((unsigned char *)ptr, old, new); case 2: return __sync_val_compare_and_swap((unsigned short *)ptr, old, new); case 4: return __sync_val_compare_and_swap((unsigned int *)ptr, old, new); #ifdef CONFIG_64BIT case 8: return __sync_val_compare_and_swap((unsigned long *)ptr, old, new); #endif } } ``` 该代码片段展示了如何通过 GCC 提供的内置函数 `_sync_val_compare_and_swap` 来实现不同大小的数据类型的比较和交换操作。具体来说,`__cmpxchg` 接受四个参数:指向要修改的目标地址 (`ptr`)、期望找到的旧值 (`old`)、希望写入的新值 (`new`) 和数据宽度 (`size`)。根据传入的不同尺寸参数,会选择合适的底层汇编指令来进行实际的比较和替换过程[^4]。 对于 x86 架构而言,其对应的机器码可能类似于下面这样: ```asm lock cmpxchgl %edx,(%eax) ``` 这里,“lock” 前缀确保了当前 CPU 核心独占总线使用权直到整个指令序列结束为止,从而防止其他处理器干扰正在进行中的事务处理流程[^5]。 #### 使用案例分析 为了更好地理解 `cmpxchg` 如何融入到更复杂的系统设计当中去解决现实世界里的问题,我们来看几个具体的例子吧! ##### 自旋锁初始化 自旋锁是一种简单的互斥原语,在等待获取锁期间不会主动放弃CPU时间片而是不断轮询直至成功占有资源为止。下面是简化版的自旋锁结构体以及相关成员变量声明方式之一: ```c typedef struct spinlock_t { volatile unsigned int slock; } spinlock_t; #define SPIN_LOCK_UNLOCKED { .slock = 0 } void spin_lock(spinlock_t *lock){ while (__sync_lock_test_and_set(&lock->slock, 1)){} } int spin_trylock(spinlock_t *lock){ return !__sync_lock_test_and_set(&lock->slock, 1); } void spin_unlock(spinlock_t *lock){ lock->slock = 0; } ``` 在这个版本里头,当尝试锁定的时候会调用 `__sync_lock_test_and_set()` 方法设置标志位的同时返回之前的状态值以便判断是否已经有人抢先一步占据了这个锁对象。解锁动作则仅仅是简单地把对应字段重置回初始状态即可[^6]。 ##### 参考计数器维护 另一个常见的应用场景涉及到动态分配的对象生命周期跟踪方面的工作——即所谓的引用计数技术。每当新增加一个对该实体的有效引用关系建立起来之后就应当相应增加它的内部统计数值;反之亦然减少次数时也要小心谨慎避免误删仍然处于活跃期内的目标实例。如下所示为一段伪代码表示法描述此类行为模式的大致轮廓特征: ```c struct kref { atomic_t refcount; }; static inline void kref_init(struct kref *krefp){ atomic_set(&(krefp)->refcount ,1 ); } bool kref_get_unless_zero(struct kref *krefp ){ return likely(atomic_inc_not_zero(&(krefp)->refcount )); } void kref_put(struct kref *krefp,void (*release)(struct kref*)){ if(unlikely(!atomic_dec_and_test(&(krefp)->refcount ))){ release(krefp); } } ``` 在这里可以看到每次增减都利用到了类似的原子级别操作手段以保障即使是在高度竞争环境下也能保持一致性约束条件得到满足的要求[^7]。 ### 结论 综上所述,`cmpxchg` 不仅是一个基础构建模块级别的工具集组成部分,而且还是许多高级抽象层面上不可或缺的重要基石构件之一。通过对它的深入学习研究可以帮助开发者们更加高效稳定可靠地开发出高质量软件产品出来。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值