自旋锁不允许读和读之间的并发,读写锁则更进了一步,允许读和读之间的并发,顺序锁又更进了一步,允许读和写之间的并发。为了实现这一需求,顺序锁在读时不上锁,也就意味着在读的期间允许写,但是在读之前需要先读取一个顺序值,读操作完成之后,再次读取顺序值,如果两者相等,说明在读的过程中没有发生写操作,否则要重新读取。显然,写操作要上锁,并且要更新顺序值。顺序锁特别适合读很多而写很少的场合,否则由于反复的读操作,也不一定能获得较高的效率。顺序锁的数据类型是seqlock_t,其类型定义如下:
typedef struct{
struct seqcount seqcount;
spinlock_t lock;
}seqlock_t;
很显然,顺序锁使用了自旋锁的机制,并且有一个顺序值seqcount,顺序锁的主要API如下:
seqlock_init(x)
unsigned read_seqbegin(const seqlock_t *sl);
unsigned read_seqretry(const seqlock_t *sl,unsigned start);
void write_seqlock(seqlock_t *sl);
void write_sequnlock(seqlock_t *sl);
void write_seqlock_bh(seqlock_t *sl);
void write_sequnlock_bh(seqlock_t *sl);
void write_seqlock_irq(seqlock_t *sl);
void write_sequnlock_irq(seqlock_t *sl);
write_seqlock_irqsave(lock,flags);
void write_sequnlock_irqrestore(seqlock_t *sl,unsigned long flags);
seqlock_t init:初始化顺序锁。
read_seqbegin:读之前获取顺序值,函数返回顺序值。
read_seqretry:读之后验证顺序值是否发生了变化,返回1表示需要重读,返回0表示读成功。
write_seqlock:写之前加锁,其他的变体参照自旋锁。
write_sequnlock:写之后解锁,其他的变体参照自旋锁。
我们可以使用下面的代码来使顺序锁对 i 的操作进行互斥:
int i = 5;
unsigned long flags;
/*定义顺序锁*/
seqlock_t lock;
/*使用之前必须初始化顺序锁*/
seqlock_init(&lock);
int v;
unsigned start;
do{
/*读之前要获取顺序值*/
start = read_seqbegin(&lock);
v = i;
/*读完之后检查顺序自是否发生了变化,如果是,则要重读*/
}while(read_seqretry(&lock,start));
/*写之前获取顺序锁*/
write_seqlock_irqsave(&lock,flags);
i++;
/*写完之后释放顺序锁*/
write_sequnlock_irqrestore(&lock,flags);