(点击上方蓝字,快速关注)
1、基本概念
原子操作可以保证指令以原子的方式执行,执行过程不被打断。它通过把读取和修改变量的行为包含在一个单步中执行,从而防止了竞争的发生,保证操作结果总是一致的。
原子操作确保对同一数据的“读取-修改-写入”操作在它的执行期间不会被打断,要么全部执行完成,要么根本不会执行。例如在ARM上对全局变量的++运算至少要经历以下三步:
ldr r3, [r3, #0]
add r2, r3, #1
str r2, [r3, #0]
这就给并发访问制造了可能,所以在编写内核代码时需要给有可能被并发访问的变量加上原子操作。
Linux内核提供了两组原子操作接口,一组用于整数,一组用于位操作。
2、原子整数操作
针对整数的原子操作只能对atomic_t类型的数据进行处理。atomic_t定义如下:
/* linux-2.6.38.8/include/linux/types.h */
typedef struct {
int counter;
} atomic_t;
针对整数的原子操作只能对atomic_t类型的数据进行处理,之所以没有用C语言的int类型,主要有两个原因:
1、让原子函数只接受atomic_t类型的操作数,可以确保原子操作只与这种特殊类型数据一起使用,防止该类型数据不会传给其它非原子操作
2、使用atomic_t类型确保编译器不对相应的值进行访问优化
3、在不同体系结构上实现原子操作的时候,使用atomic_t可以屏蔽其间的差异。
尽管Linux的整型数据都是32位的,但是使用atomic_t的代码只能将将该类型的数据当作24位来用,我们看看atomic_t的数据
具体的接口API函数整理如下:
接口函数 | 描述 |
static inline void atomic_add(int i, atomic_t *v) | 给一个原子变量v增加i |
static inline int atomic_add_return(int i, atomic_t *v) | 同上,只不过将变量v的最新值返回 |
static inline void atomic_sub(int i, atomic_t *v) | 给一个原子变量v减去i |
static inline int atomic_sub_return(int i, atomic_t *v) | 同上,只不过将变量v的最新值返回 |
static inline int atomic_cmpxchg(atomic_t *ptr, int old, int new) | 比较old和原子变量ptr中的值,如果相等,那么就把new值赋给原子变量。 |
atomic_read | 获取原子变量的值 |
atomic_set | 设定原子变量的值 |
atomic_inc(v) | 原子变量的值加一 |
atomic_inc_return(v) | 同上,只不过将变量v的最新值返回 |
atomic_dec(v) | 原子变量的值减去一 |
atomic_dec_return(v) | 同上,只不过将变量v的最新值返回 |
atomic_sub_and_test(i, v) | 给一个原子变量v减去i,并判断变量v的最新值是否等于0 |
atomic_add_negative(i,v) | 给一个原子变量v增加i,并判断变量v的最新值是否是负数 |
static inline int atomic_add_unless(atomic_t *v, int a, int u) | 只要原子变量v不等于u,那么就执行原子变量v加a的操作。 |
3、原子位操作
除了原子整数操作外,内核还提供了一组针对位这一级数据进行操作的函数,位操作函数是对普通的内在地址进行操作的,它的参数是一个指针和一个位号。由于是对普通的指针进程操作,所以没有像atomic_t这样的类型约束。
unsigned long word = 0;
set_bit(0,&word);
clear_bit(0,&word);
change_bit(0,&word);//翻转第0位的值
原子整数操作 | 描述 |
void set_bit(int nr,void *addr) | 原子地设置addr所指对象的第nr位 |
void clear_bit(int nr,void *addr) | 原子地清空addr所指对象的第nr位 |
void change_bit(int nr,void *addr) | 原子地翻转addr所指对象的第nr位 |
int test_and_set_bit(int nr,void *addr) | 原子地设置addr所指对象的第nr位,并返回原先的值 |
int test_and_clear_bit(int nr,void *addr) | 原子地清空addr所指对象的第nr位,并返回原先的值 |
int test_and_change_bit(int nr,void *addr) | 原子地翻转addr所指对象的第nr位,并返回原先的值 |
int test_bit(int nr,void *addr) | 原子地返回addr所指对象的第nr位 |