Linux驱动之顺序锁

时间:2021-07-04 23:37:42

顺序锁是一种轻量级锁,一般用于读多、写少。当写时总能获取到锁,读时需要忙等待直到写操作结束。

typedef struct {
	unsigned sequence;
	spinlock_t lock;
} seqlock_t;

声明:

seqlock_t seq=SEQLOCK_UNLOCKED;

DEFINE_SEQLOCK(seq);

seqlock_t seq;
seqlock_init(&seq);

写锁:

/* Lock out other writers and update the count.
 * Acts like a normal spin_lock/unlock.
 * Don't need preempt_disable() because that is in the spin_lock already.
 */
static inline void write_seqlock(seqlock_t *sl)
{
	spin_lock(&sl->lock);
	++sl->sequence;
	smp_wmb();
}

static inline void write_sequnlock(seqlock_t *sl)
{
	smp_wmb();
	sl->sequence++;
	spin_unlock(&sl->lock);
}

static inline int write_tryseqlock(seqlock_t *sl)
{
	int ret = spin_trylock(&sl->lock);

	if (ret) {
		++sl->sequence;
		smp_wmb();
	}
	return ret;
}

读锁:

/* Start of read calculation -- fetch last complete writer token */
static __always_inline unsigned read_seqbegin(const seqlock_t *sl)
{
	unsigned ret;

repeat:
	ret = sl->sequence;
	smp_rmb();
	if (unlikely(ret & 1)) {
		cpu_relax();
		goto repeat;
	}

	return ret;
}

/*
 * Test if reader processed invalid data.
 *
 * If sequence value changed then writer changed data while in section.
 */
static __always_inline int read_seqretry(const seqlock_t *sl, unsigned start)
{
	smp_rmb();

	return (sl->sequence != start);
}

从上可以看出,写操作时,获取锁和释放锁只要配对,序号的值肯定是偶数。

读操作时,并没有锁的操作,而是只要判定序号的奇偶来判定是否有写操作在进行。

因此,可以认为读操作可以随时访问资源,只不过需要检查是否和写入者发生冲突,当发生冲突时,需要对资源重新访问。


另外,顺序锁可以在中断上下文中使用,原因是写操作使用的是自旋锁,读操作没有使用任何锁机制。不过和自旋锁一样,中断中必须使用禁止中断的版本。