原子操作
Linux内核提供了多种同步机制,这些机制本质上都是通过原子操作来实现的。原子操作可以保证指令以原子方式执行,不会被中途打断(中断也不会打断一个指令,处理器只有在当前指令完成后才会去处理中断)。内核提供了两套原子操作的接口,一套用于整数原子操作,一套用于进行位原子操作。这些接口的实现是和架构相关的,Linux系统支持的所有架构都实现了这些接口。大部分架构为简单的算术运算提供了原子版本的指令。有些架构缺乏直接的原子操作指令,但是提供了锁内存总线的命令来锁定内存总线,确保其他可能修改内存的操作无法同时执行。有些操作天生就是原子指令,比如读取一个word大小的变量,有些操作不是原子指正,比如对整数加1,x86架构中提供了lock指令前缀,用于锁定特定内存地址,确保在多处理器系统中能够互斥地使用这个内存地址,阻止其他处理器在当前处理器的lock前缀的指令执行期间访问锁定的内存地址,从而保证原子性。
整数的原子操作
linux使用atomic_t类型来表示用于原子操作的整数,这样可以确保原子操作函数只接受原子整数变量,同时确保原子整数变量不会被传给其他函数,此外atomic_t类型可以屏蔽不同架构的差异。32bit机器和64bit机器中的atomic_t类型都是32bit,64bit机器还提供了64bit的版本atomic64_t。下面只讨论32bit的atomic_t,其定义如下:
typedef struct {
volatile int counter;
}atomic_t;
volatile声明告诉编译器不要对这个值的访问进行优化,从而确保每次原子操作获得的都是正确的内存地址,而不是一个别名。
下表列举了部分整数的原子操作方法,定义在<asm/atomic.h>中
原子操作函数 | 功能 |
---|---|
ATOMIC_INIT(int i) | At declaration, initialize to i. |
int atomic_read(atomic_t *v) | Atomically read the integer value of v. |
void atomic_set(atomic_t *v, int i) | Atomically set v equal to i. |
void atomic_add(int i, atomic_t *v) | Atomically add i to v. |
void atomic_sub(int i, atomic_t *v) | Atomically subtract i from v. |
void atomic_inc(atomic_t *v) | Atomically add one to v. |
void atomic_dec(atomic_t *v) | Atomically subtract one from v. |
int atomic_sub_and_test(int i, atomic_t *v) | Atomically subtract i from v and return true if the result is zero; otherwise false |
下面是几个use case:
atomic_t v; //定义一个原子整数变量v
atomic_t u = ATOMIC_INIT(0); //定义一个原子整数变量u,并将其初始化为0
atomic_set(&v, 4); //将v设置为4
atomic_add(2, &v); //给v加2,v变为6
atomic_inc(&v); //给v加1,v变为7
printk(“%d\n”, atomic_read(&v)); //将原子整数转化成普通整型,并打印,将打印7
位的原子操作
和普通的位操作类似,只是需要使用原子操作函数,不能用位操作符,下表列出了位原子操作的函数:
原子操作函数 | 功能 |
---|---|
void set_bit(int nr, void *addr) | Atomically set the nr-th bit starting from addr. |