读写锁特性:
一次只有一个线程可以占有写模式的读写锁, 但是可以有多个线程同时占有读模式的读写锁. 正是因为这个特性,
当读写锁是写加锁状态时, 在这个锁被解锁之前, 所有试图对这个锁加锁的线程都会被阻塞.
当读写锁在读加锁状态时, 所有试图以读模式对它进行加锁的线程都可以得到访问权, 但是如果线程希望以写模式对此锁进行加锁, 它必须直到所有的线程释放锁.
通常, 当读写锁处于读模式锁住状态时, 如果有另外线程试图以写模式加锁, 读写锁通常会阻塞随后的读模式锁请求, 这样可以避免读模式锁长期占用, 而等待的写模式锁请求长期阻塞.
读写锁适合于对数据结构的读次数比写次数多得多的情况. 因为, 读模式锁定时可以共享, 以写模式锁住时意味着独占, 所以读写锁又叫共享-独占锁.
接下来我们看下DPDK 源码
结构定义:
typedef struct {
volatile int32_t cnt; /**< -1 when W lock held, > 0 when R locks held. */
} rte_rwlock_t;
注:volatile 所修饰的变量随时都可能发生变化,每次使用该值必须从内存地址中读取,而不是直接从寄存器中拷贝内容。
// 写锁,会一直循环,除非原子已经控制住
static inline void
rte_rwlock_write_lock(rte_rwlock_t rwl)
{
int32_t x;
int success = 0;
while (success == 0) {
x = rwl->cnt;
/ a lock is held */
if (x != 0) {
rte_pause();
continue;
}
success = rte_atomic32_cmpset((volatile uint32_t *)&rwl->cnt,
0, -1);
}
}
写锁会一直循环来获取变量状态,判断有读,有写,自己就循环,否则,把原子设置为-1,
static inline void
rte_rwlock_write_unlock(rte_rwlock_t *rwl)
{
rte_atomic32_inc((rte_atomic32_t *)(intptr_t)&rwl->cnt);
}
解锁是原子操作+1
注:ret_pause() PAUSE指令给处理器提了个醒:这段代码序列是个循环等待。处理器利用这个提示可以避免在大多数情况下的内存顺序违规,这将大幅提升性能。因为这个原因,所以推荐在循环等待中使用PAUSE指令。
原子操作函数
rte_atomic32_cmpset(volatile uint32_t *dst, uint32_t exp, uint32_t src)
{
unsigned int ret = 0;
asm volatile(
"\tlwsync\n"
"1:\tlwarx %[ret], 0, %[dst]\n"
"cmplw %[exp], %[ret]\n"
"bne 2f\n"
"stwcx. %[src], 0, %[dst]\n"
"bne- 1b\n"
"li %[ret], 1\n"
"b 3f\n"
"2:\n"
"stwcx. %[ret], 0, %[dst]\n"
"li %[ret], 0\n"
"3:\n"
"isync\n"
: [ret] "=&r" (ret), "=m" (*dst)
: [dst] "r" (dst),
[exp] "r" (exp),
[src] "r" (src),
"m" (*dst)
: "cc", "memory");
return ret;
}
static inline void
rte_rwlock_read_lock(rte_rwlock_t *rwl)
{
int32_t x;
int success = 0;
while (success == 0) {
x = rwl->cnt;
/* write lock is held */
if (x < 0) {
rte_pause();
continue;
}
success = rte_atomic32_cmpset((volatile uint32_t *)&rwl->cnt,
x, x + 1);
}
}
#读锁,判断是否有在写的操作,如果有的话循环,如果没有的话+1 操作
static inline void
rte_rwlock_read_lock(rte_rwlock_t *rwl)
{
int32_t x;
int success = 0;
while (success == 0) {
x = rwl->cnt;
/* write lock is held */
if (x < 0) {
rte_pause();
continue;
}
success = rte_atomic32_cmpset((volatile uint32_t *)&rwl->cnt,
x, x + 1);
}
}
static inline void
rte_rwlock_read_unlock(rte_rwlock_t *rwl)
{
rte_atomic32_dec((rte_atomic32_t *)(intptr_t)&rwl->cnt);
}
解读锁只需要减一即可
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。