当需要同步的数据比较简单,且是多读少写,且写优先的,情况下
你可以使用 seqlock
seqlock 的底层是用spin实现,所以自然继承了spin的特性
如: 禁止抢占,sleep 等
typedef struct {
unsigned sequence;
spinlock_t lock;
} seqlock_t;
序列号初始化为0,即偶数,代表没有上锁
#define seqlock_init(x) \
do { \
(x)->sequence = 0; \
spin_lock_init(&(x)->lock); \
} while (0)
写操作,比较简单,只是对序列号++而已,而这个正式精华
(想想为什么不是++ —— 而只是++)
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);
}
使用例子:
write_seqlock(&xtime_lock);
// 做些写的操作
// 。。。。。。。
write_sequnlock(&xtime_lock);
关键是seqlock对数据的读实现及读取方式
static __always_inline unsigned read_seqbegin(const seqlock_t *sl)
{
unsigned ret;
repeat:
ret = sl->sequence;
smp_rmb();
// 偶数代表锁释放(初始值为0)
// sequence只要 ++ 就够了
if (unlikely(ret & 1)) {
cpu_relax();
goto repeat;
}
return ret;
}
// 加入序列号有改变的话,说明在这段时间里面写操作有可以改变了数据
static __always_inline int read_seqretry(const seqlock_t *sl, unsigned start)
{
smp_rmb();
return (sl->sequence != start);
}
// 举个栗子
// 参考 /kernel/kernel/hrtimer.c
static void hrtimer_get_softirq_time(struct hrtimer_cpu_base *base)
{
ktime_t xtim, tomono;
struct timespec xts, tom;
unsigned long seq;
// 两次返回的值不一样则一致循环
// 比较 sequence
// 这样的好处是,对于写没有影响
do {
seq = read_seqbegin(&xtime_lock);
xts = __current_kernel_time();
tom = wall_to_monotonic;
} while (read_seqretry(&xtime_lock, seq));
................
}