读 - 写自旋锁
一个或多个任务可以并发地持有读者锁;相反,用于写的锁最多只能被一个写任务持有,而且此时不能有并发地读操作。
读/写锁也叫做共享/排斥锁,或者并发/排斥锁,因为这种锁对读者而言是共享地,对写者以排斥形式获取地。
基本数据结构
在内核代码中,读-写自旋锁用rwlock_t类型表示,
typedef struct {
/**
* 这个锁标志与自旋锁不一样,自旋锁的lock标志只能取0和1两种值。
* 读写自旋锁的lock分两部分:
* 0-23位:表示并发读的数量。数据以补码的形式存放。
* 24位:未锁标志。如果没有读或写时设置该,否则清0
* 注意:如果自旋锁为空(设置了未锁标志并且无读者),则lock字段为0x01000000
* 如果写者获得了锁,则lock为0x00000000(未锁标志清0,表示已经锁,但是无读者)
* 如果一个或者多个进程获得了读锁,那么lock的值为0x00ffffff,0x00fffffe等(未锁标志清0,后面跟读者数量的补码)
*/
volatile unsigned int lock;
#ifdef CONFIG_DEBUG_SPINLOCK
unsigned magic;
#endif
#ifdef CONFIG_PREEMPT
/**
* 表示进程正在忙等待自旋锁。
* 只有内核支持SMP和内核抢占时才使用本标志。
*/
unsigned int break_lock;
#endif
} rwlock_t;
rwlock_t中的锁标志与自旋锁不同,
(注:如果自旋锁为空(设置了未锁标志并且无读者),则锁字节位0x0100 0000)(自锁锁的锁标志只能取0和1两种值。
读写自旋锁的锁分为两部分:
· 0-23位:表示并发读的数量;
·第24位:未锁标志如果没有读或写时会设置,否则清0。
如果写者获得了锁,则锁为0x0000 0000(未锁标志清0,表示已经锁,但无读者);如果一个或多个进程获得了读锁,那么锁的值为0x00ff ffff,0x00ff fffe锁标志清0)。
初始化
rwlock_init(),初始化指定的rwlock_t。
#define rwlock_init(x) do { *(x) = RW_LOCK_UNLOCKED; } while(0)
read_lock
获得指定的读锁。在没有配置内核抢占时,read_lock的实现如下,
/**
* 在没有配置内核抢占时,read_lock的实现。
*/
void __lockfunc _read_lock(rwlock_t *lock)
{
preempt_disable();
_raw_read_lock(lock);
}
EXPORT_SYMBOL(_read_lock);
接下来,read_lock调用_raw_read_lock(),其中第一个参数为读写锁指针,第二个为获取读锁失败时的处理函数的函数指针。
/**
* 在没有配置内核抢占时,read_lock调用它。
*/
static inline void _raw_read_lock(rwlock_t *rw)
{
#ifdef CONFIG_DEBUG_SPINLOCK
BUG_ON(rw->magic != RWLOCK_MAGIC);
#endif
__build_read_lock(rw, "__read_lock_failed");
}
__build_read_lock
函数raw_read_lock调用宏函数__build_read_lock,
这里的__builtin_constant_p()是编译器的GCC的内置函数,用于判断一个值是否为编译时常量,如果是,函数返回1,否则返回0。
#define __build_read_lock(rw, helper) do { \
if (__builtin_constant_p(rw)) \
__build_read_lock_const(rw, helper); \
else \
__build_read_lock_ptr(rw, helper); \
} while (0)
__build_read_lock_ptr和__build_read_lock_const
在没有内核抢占时,助手为__read_lock_failed。
/**
* 在没有内核抢占时,read_lock会调到这里来。
* 在那种情况下,helper为__read_loc