目录
1. 原子操作
gcc从4.1.2开始提供了__sync_*系列的build-in函数,用于提供加减和逻辑运算的原子操作,主要接口的其声明如下:
- type __sync_fetch_and_add (type *ptr, type value, ...): 将value加到ptr上,结果更新到ptr,并返回操作之前*ptr的值
- type __sync_fetch_and_sub (type *ptr, type value, ...):从ptr减去value,结果更新到ptr,并返回操作之前*ptr的值
- type __sync_fetch_and_or (type *ptr, type value, ...): 将ptr与value相或,结果更新到ptr, 并返回操作之前*ptr的值
- type __sync_fetch_and_and (type *ptr, type value, ...): 将ptr与value相与,结果更新到ptr,并返回操作之前ptr的值
- +type __sync_fetch_and_xor (type *ptr, type value, ...):将ptr与value异或,结果更新到ptr,并返回操作之前ptr的值
- type __sync_fetch_and_nand (type *ptr, type value, ...): 将ptr取反后,与value相与,结果更新到ptr,并返回操作之前ptr的值
- +type __sync_add_and_fetch (type *ptr, type value, ...):将value加到ptr上,结果更新到ptr,并返回操作之后新ptr的值
- type __sync_sub_and_fetch (type *ptr, type value, ...):从ptr减去value,结果更新到ptr,并返回操作之后新*ptr的值
- type __sync_or_and_fetch (type *ptr, type value, ...):将ptr与value相或, 结果更新到ptr,并返回操作之后新*ptr的值
- type __sync_and_and_fetch (type *ptr, type value, ...):将ptr与value相与,结果更新到ptr,并返回操作之后新*ptr的值
- type __sync_xor_and_fetch (type *ptr, type value, ...):将ptr与value异或,结果更新到ptr,并返回操作之后新ptr的值
- +type __sync_nand_and_fetch (type *ptr, type value, ...):将ptr取反后,与value相与,结果更新到ptr,并返回操作之后新ptr的值
- bool __sync_bool_compare_and_swap (type *ptr, type oldval type newval, ...):比较*ptr与oldval的值,如果两者相等,则将newval更新到*ptr并返回true
- type __sync_val_compare_and_swap (type *ptr, type oldval type newval, ...):比较ptr与oldval的值,如果两者相等,则将newval更新到ptr并返回操作之前*ptr的值
- __sync_synchronize (...):发出完整内存栅栏
- +type __sync_lock_test_and_set (type *ptr, type value, ...): 将value写入ptr,对ptr加锁,并返回操作之前*ptr的值。即,try spinlock语义
- void __sync_lock_release (type *ptr, ...):将0写入到ptr,并对ptr解锁。即,unlock spinlock语义
2. 锁是干嘛的
锁解决的问题:为了并行中的正确性(共享数据一致性)
多核处理器,多个CPU如果同时访问一些共享资源,可能会导致数据不一致,即共享数据用锁设置成多CPU分时访问。
锁带来的问题:并行变串行,效率降低
3. 锁的实现
上锁:
__sync_lock_test_and_set
通过原子操作赋值,也就是锁只能一方获取。
解锁:
__sync_lock_release
通过原子操作清0。
锁的操作均是原子操作,但是实现锁的功能,还需要内存屏蔽功能,保证临界区的执行可靠性。某些平台的部分代码:
#define SPINLOCK_INIT \
{ \
0 \
}
#define CORELOCK_INIT \
{ \
.lock = SPINLOCK_INIT, \
.count = 0, \
.core = -1 \
}
/* Defination of memory barrier macro */
#define mb() \
{ \
asm volatile("fence" :: \
: "memory"); \
}
#define atomic_set(ptr, val) (*(volatile typeof(*(ptr)) *)(ptr) = val)
#define atomic_read(ptr) (*(volatile typeof(*(ptr)) *)(ptr))
#define atomic_get(ptr) (*(volatile typeof(*(ptr)) *)(ptr))
#ifndef __riscv_atomic
#error "atomic extension is required."
#endif
#define atomic_add(ptr, inc) __sync_fetch_and_add(ptr, inc)
#define atomic_sub(ptr, inc) __sync_fetch_and_sub(ptr, inc)
#define atomic_sub_return(ptr, inc) __sync_sub_and_fetch (ptr, inc)
#define atomic_or(ptr, inc) __sync_fetch_and_or(ptr, inc)
#define atomic_swap(ptr, swp) __sync_lock_test_and_set(ptr, swp)
#define atomic_cas(ptr, cmp, swp) __sync_val_compare_and_swap(ptr, cmp, swp)
typedef struct _spinlock
{
int lock;
} spinlock_t;
typedef struct _semaphore
{
spinlock_t lock;
int count;
int waiting;
} semaphore_t;
typedef struct _corelock
{
spinlock_t lock;
int count;
int core;
} corelock_t;
static inline int spinlock_trylock(spinlock_t *lock)
{
int res = atomic_swap(&lock->lock, -1);
/* Use memory barrier to keep coherency */
mb();
return res;
}
static inline void spinlock_lock(spinlock_t *lock)
{
while(spinlock_trylock(lock))
;
}
static inline void spinlock_unlock(spinlock_t *lock)
{
/* Use memory barrier to keep coherency */
mb();
atomic_set(&lock->lock, 0);
asm volatile("nop");
}