备注:
1 本笔记是作者根据自己的理解简化和总结的,可能有遗漏或者错误,还请各位路过的大拿们指正。
2 本笔记中的所有截图版权归属于原作者所有。
作者个人认为Linux实现的顺序锁内部逻辑非常清晰,如果以后需要山寨一个,非常值得借鉴。
#ifndef __LINUX_SEQLOCK_H #define __LINUX_SEQLOCK_H /* * Reader/writer consistent mechanism without starving writers. This type of * lock for data where the reader wants a consitent set of information * and is willing to retry if the information changes. Readers never * block but they may have to retry if a writer is in * progress. Writers do not wait for readers. * * This is not as cache friendly as brlock. Also, this will not work * for data that contains pointers, because any writer could * invalidate a pointer that a reader was following. * * Expected reader usage: * do { * seq = read_seqbegin(&foo); * ... * } while (read_seqretry(&foo, seq)); * * * On non-SMP the spin locks disappear but the writer still needs * to increment the sequence variables because an interrupt routine could * change the state of the data. * * Based on x86_64 vsyscall gettimeofday * by Keith Owens and Andrea Arcangeli */ #include <linux/config.h> #include <linux/spinlock.h> #include <linux/preempt.h> /* 修改线程: write_seqlock(seq); 修改keyvalue write_sequnlock(seq); 读取线程: do { seq = read_seqbegin(&foo); 读取keyvalue } while (read_seqretry(&foo, seq)); */ /* 顺序结构体很简单,就一个计数器和一个锁。 计数器用来在读取的时候判断下是否有修改。 锁则是用来对写操作进行锁定。 */ typedef struct { unsigned sequence; spinlock_t lock; } seqlock_t; /* * These macros triggered gcc-3.x compile-time problems. We think these are * OK now. Be cautious. */ /* 初始化一个顺序锁 */ #define SEQLOCK_UNLOCKED { 0, SPIN_LOCK_UNLOCKED } #define seqlock_init(x) do { *(x) = (seqlock_t) SEQLOCK_UNLOCKED; } while (0) /* 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) { /*先对内部的spinlock加锁,防止多线程调用write_seqlock 时产生并发问体*/ 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 inline unsigned read_seqbegin(const seqlock_t *sl) { unsigned ret = sl->sequence; smp_rmb(); return ret; } /* Test if reader processed invalid data. * If initial values is odd, * then writer had already started when section was entered * If sequence value changed * then writer changed data while in section * * Using xor saves one conditional branch. */ /* 再次读取下锁中的计数器值,看下有没有被修改,如果被修改,那就 说明在read_seqbegin和read_seqretry执行之间有修改操作,需要重新读取。 */ static inline int read_seqretry(const seqlock_t *sl, unsigned iv) { smp_rmb(); return (iv & 1) | (sl->sequence ^ iv); } /* * Version using sequence counter only. * This can be used when code has its own mutex protecting the * updating starting before the write_seqcountbeqin() and ending * after the write_seqcount_end(). */ /* 顺序锁的简化版本,由外部代码保证写操作是不会出现并行情况 */ typedef struct seqcount { unsigned sequence; } seqcount_t; #define SEQCNT_ZERO { 0 } #define seqcount_init(x) do { *(x) = (seqcount_t) SEQCNT_ZERO; } while (0) /* Start of read using pointer to a sequence counter only. */ static inline unsigned read_seqcount_begin(const seqcount_t *s) { unsigned ret = s->sequence; smp_rmb(); return ret; } /* Test if reader processed invalid data. * Equivalent to: iv is odd or sequence number has changed. * (iv & 1) || (*s != iv) * Using xor saves one conditional branch. */ static inline int read_seqcount_retry(const seqcount_t *s, unsigned iv) { smp_rmb(); return (iv & 1) | (s->sequence ^ iv); } /* * Sequence counter only version assumes that callers are using their * own mutexing. */ static inline void write_seqcount_begin(seqcount_t *s) { s->sequence++; smp_wmb(); } static inline void write_seqcount_end(seqcount_t *s) { smp_wmb(); s->sequence++; } /* * Possible sw/hw IRQ protected versions of the interfaces. */ #define write_seqlock_irqsave(lock, flags) \ do { local_irq_save(flags); write_seqlock(lock); } while (0) #define write_seqlock_irq(lock) \ do { local_irq_disable(); write_seqlock(lock); } while (0) #define write_seqlock_bh(lock) \ do { local_bh_disable(); write_seqlock(lock); } while (0) #define write_sequnlock_irqrestore(lock, flags) \ do { write_sequnlock(lock); local_irq_restore(flags); } while(0) #define write_sequnlock_irq(lock) \ do { write_sequnlock(lock); local_irq_enable(); } while(0) #define write_sequnlock_bh(lock) \ do { write_sequnlock(lock); local_bh_enable(); } while(0) #define read_seqbegin_irqsave(lock, flags) \ ({ local_irq_save(flags); read_seqbegin(lock); }) #define read_seqretry_irqrestore(lock, iv, flags) \ ({ \ int ret = read_seqretry(lock, iv); \ local_irq_restore(flags); \ ret; \ }) #endif /* __LINUX_SEQLOCK_H */
发表评论