14.3 原子操作

能够执行原子操作的原子变量只有整型,包括无符号整型ngx_atomic_uint_t和有符号整型ngx_atomic_t,这两种类型都使用了volatile关键字告诉C编译器不要做优化。

想要使用原子操作来修改、获取整型变量,自然不能使用加减号,而要使用Nginx提供的两个方法:ngx_atomic_cmp_set和ngx_atomic_fetch_add。这两个方法都可以用来修改原子变量的值,而ngx_atomic_cmp_set方法同时还可以比较原子变量的值,下面具体看看这两个方法。


static ngx_inline ngx_atomic_uint_t

ngx_atomic_cmp_set(ngx_atomic_t*lock,ngx_atomic_uint_t old,

ngx_atomic_uint_t set)


ngx_atomic_cmp_set方法会将old参数与原子变量lock的值做比较,如果它们相等,则把lock设为参数set,同时方法返回1;如果它们不相等,则不做任何修改,返回0。


static ngx_inline ngx_atomic_int_t

ngx_atomic_fetch_add(ngx_atomic_t*value,ngx_atomic_int_t add)


ngx_atomic_fetch_add方法会把原子变量value的值加上参数add,同时返回之前value的值。

在Nginx各种锁的实现中,可以看到原子变量和这两个方法的多种用法。

即使操作系统的内核无法提供原子性的操作,那么Nginx也会对上述两个方法提供一种实现,这在14.3.1节中会简单说明;对于各种硬件体系架构,原子操作的实现不尽相同,在14.3.2节中将会以最常见的X86架构为例,说明Nginx是怎样实现上述两个原子操作方法的。在14.3.3节,介绍Nginx封装的ngx_spinlock自旋锁是怎样使用原子变量实现的。

14.3.1 不支持原子库下的原子操作

当无法实现原子操作时,就只能用volatile关键字在C语言级别上模拟原子操作了。事实上,目前绝大多数体系架构都是支持原子操作的,给出这一节内容更多的是方便读者理解ngx_atomic_cmp_set方法和ngx_atomic_fetch_add方法的意义。先来看看ngx_atomic_cmp_set方法的实现,如下所示。


static ngx_inline ngx_atomic_uint_t

ngx_atomic_cmp_set(ngx_atomic_t*lock,ngx_atomic_uint_t old,

ngx_atomic_uint_t set)

{

//当原子变量lock与old相等时,才能把set设置到lock中并返回1

if(*lock==old){

*lock=set;

return 1;

}

//若原子变量lock与old不相等,则返回0

return 0;

}


ngx_atomic_fetch_add方法的实现也很简单,如下所示。


static ngx_inline ngx_atomic_int_t

ngx_atomic_fetch_add(ngx_atomic_t*value,ngx_atomic_int_t add)

{

ngx_atomic_int_t old;

//将原子变量value加上add值之后,再返回原先value的值

old=*value;

*value+=add;

return old;

}