当需要同步的数据比较简单,且是多读少写,且写优先的,情况下
你可以使用 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)); ................ }