Linux内核常用接口和宏(三)test_and_set_bit
·
test_and_set_bit 是 Linux 内核中一个经典的原子操作函数,作用是将指定位置 1,并返回该位的旧值。不同架构有不同的实现方式
int test_and_set_bit(int nr, volatile unsigned long *addr);
nr:要设置的位编号(从 0 开始)addr:位图起始地址,本质是一个unsigned long类型数组的起始地址- 返回值:该位设置前的旧值(0 表示原来未设置,非 0 表示原来已设置)
通用 C 实现(基于自旋锁)
在不支持硬件原子位操作的架构上,通过锁来保证原子性:
1static inline int test_and_set_bit(int nr, volatile unsigned long *addr)
2{
3 unsigned long mask = BIT_MASK(nr);
4 unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
5 unsigned long old;
6 unsigned long flags;
7
8 _atomic_spin_lock_irqsave(p, flags);
9 old = *p; // 读取旧值
10 *p = old | mask; // 设置目标位
11 _atomic_spin_unlock_irqrestore(p, flags);
12
13 return (old & mask) != 0;
14}
addr数组的元素是unsigned long,所以mask需要计算一个unsigned long长度内的mask
p= ((unsigned long *)addr) + BIT_WORD(nr)这一步得到的是addr数组中第(BIT_WORD(nr))个元素的首地址;
其中辅助宏定义为:
1#define BIT_MASK(nr) (UL(1) << ((nr) % BITS_PER_LONG))
2#define BIT_WORD(nr) ((nr) / BITS_PER_LONG)
BIT_MASK(nr):生成只包含目标位的掩码,因为bitmap数组的每个元素是unsigned long,所以求余的除数是unsigned long的长度。BIT_WORD(nr):计算nr位于addr数组的第几个unsigned long元素中,之所以要这样计算,是因为p和addr都是unsigned long类型的指针,指针的加法是偏移n * sizeof(指针指向的类型),所以上述操作就是把nr转换成bitmap数组里的一个元素里进行操作
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)