Linux内核的原子操作

1、基本概念

原子操作可以保证指令以原子的方式执行,执行过程不被打断。它通过把读取和修改变量的行为包含在一个单步中执行,从而防止了竞争的发生,保证操作结果总是一致的。

例如:

int i=9;

线程1:

i++

i=9 OR i=8

线程2

i–;

i=9 OR i=8

两个线程并发的执行,导致结果不确定性。原子操作的作用和信号量机制是一样,都是为了防止同时访问临界资源,保证结果的一致性。大多数硬件体系结构要么本来就支持简单的原子操作,要么就为音频执行提供了锁内在总线的指令,例如x86平台上,就支持CPU锁总线操作,汇编指令前缀“LOCK”就可以将总线锁作,直到指令结束时锁打开;而有些硬件体系结构本身就不太支持原子操作,比如SPARC,但是Linux内核通过一些方法,做到了原子操作。

原子操作在Linux内核里分为原子整数操作和原子位操作,下面我们来看看这两个操作用法。

2、原子整数操作

针对整数的原子操作只能对atomic_t类型的数据进行处理,之所以没有用C语言的int类型,主要有两个原因:

1、让原子函数只接受atomic_t类型的操作数,可以确保原子操作只与这种特殊类型数据一起使用,防止该类型数据不会传给其它非原子操作

2、使用atomic_t类型确保编译器不对相应的值进行访问优化

3、在不同体系结构上实现原子操作的时候,使用atomic_t可以屏蔽其间的差异。

尽管Linux的整型数据都是32位的,但是使用atomic_t的代码只能将将该类型的数据当作24位来用,我们看看atomic_t的数据

|+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|

|      带符号24位整形数据        |   锁   |

|+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|

|10987654321098765432109876543210|

| 3         2         1          |

|            32位atmoic_t         |

|+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|

我们看到atomic_t中嵌入了一个8bit的锁,因为SPARC体系结构对原子操作缺乏指令级的支持,所以只能使用利用该锁来避免对原子类型数据的并发访问,对于原子操作的使用,看下面例子:

1 int a=3;
2 atomic_t v=ATOMIC_INIT(a);
3 atomic_add(&v,a);
4 atomic_inc(&v);

在上面例子中,我们先定义并初始化了一个atomic_t变量,再对其进行了加法、自加操作。在Linux内核中提供了一系统的原子整数操作函数。

原子整数操作 描述
ATOMIC_INIT(int i) 在声明一个atmoic_t变量时,将它初始化为i
ATOMIC_INIT(int i) 在声明一个atmoic_t变量时,将它初始化为i
int atmoic_read(atmoic_t *v) 原子地读取整数变量v
void atmoic_set(atmoic_t *v,int i) 原子地设置v值为i
void atmoic_add(atmoic_t *v,int i) 原子地从v值加i
void atmoic_sub(atmoic_t *v,int i) 原子地从v值减i
void atmoic_inc(atmoic_t *v) 原子地从v值加1
void atmoic_dec(atmoic_t *v) 原子地从v值减1
int atmoic_sub_and_test(int i,atmoic_t *v) 原子地从v值减i,如果结果等于0返回真,否则返回假
int atmoic_add_negative(int i,atmoic_t *v) 原子地从v值减i,如果结果是负数返回真,否则返回假
int atmoic_dec_and_test(atmoic_t *v) 原子地给v减1,如果结果等于0返回真,否则返回假
int atmoic_inc_and_test(atmoic_t *v) 原子地给v加1,如果结果等于0返回真,否则返回假

原子操作最常见的用途就是实现计数器,使用复杂的锁机制来保护一个单纯的计数是很笨拙的,原子操作比起复杂的同步方法来说,给系统带来的开销小,对高速缓存行的影响也小。

3、原子位操作

除了原子整数操作外,内核还提供了一组针对位这一级数据进行操作的函数,位操作函数是对普通的内在地址进行操作的,它的参数是一个指针和一个位号。由于是对普通的指针进程操作,所以没有像atomic_t这样的类型约束。

1 unsigned long word = 0;
2 set_bit(0,&word);
3 clear_bit(0,&word);
4 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位

你可能感兴趣的:(Linux,精华文章)