做电影网站如何推广,用python做网站多吗,vps做网站空间,长宁区企业网站建设第四十章 linux-并发解决方法四#xff08;顺序锁seqlock#xff09; 文章目录第四十章 linux-并发解决方法四#xff08;顺序锁seqlock#xff09;顺序锁的设计思想是#xff0c;对某一共享数据读取时不加锁#xff0c;写的时候加锁。为了保证读取的过程中不会因为写入名…第四十章 linux-并发解决方法四顺序锁seqlock 文章目录第四十章 linux-并发解决方法四顺序锁seqlock顺序锁的设计思想是对某一共享数据读取时不加锁写的时候加锁。为了保证读取的过程中不会因为写入名的出现导致该共享数据的更新需要在读取者和写入者之间引入一整型变量称为顺序值sequence。读取者在开始读取前读取该sequence在读取后再重读该值如果与之前读取到的值不一致则说明本次读取作过程中发生了数据更新读取操作无效因此要求写入者在开始写入的时候要更新sequence的值。
typedef struct {struct seqcount seqcount;spinlock_t lock;
} seqlock_t;无符号型整数sequence用来协调读取者与写入者的作spinlock变量lock在多个写入者之间做互斥使用。 程序中如果想静态定义一个seqlock并同时初始化可以使用DEFINESE_QLOCK宏该宏会定义一个seqlock_t型变量并初始化其sequence为0lock为0
#define __SEQLOCK_UNLOCKED(lockname) \{ \.seqcount SEQCNT_ZERO(lockname), \.lock __SPIN_LOCK_UNLOCKED(lockname) \}#define DEFINE_SEQLOCK(x) \seqlock_t x __SEQLOCK_UNLOCKED(x)如果要动态初始化一个seqlock变量可以使用seqlock_init
#define seqlock_init(x)
do {
seqcount_init((x)-seqcount);
spin_lock_init((x)-lock);
} while (0)下面看看写入者在seqlock上的上锁操作write_seqlock
static inline void write_seqlock(seqlock_t *sl)
{spin_lock(sl-lock);write_seqcount_begin(sl-seqcount);
}static inline void write_seqcount_begin(seqcount_t *s)
{write_seqcount_begin_nested(s, 0);
}static inline void write_seqcount_begin_nested(seqcount_t *s, int subclass)
{raw_write_seqcount_begin(s);seqcount_acquire(s-dep_map, subclass, 0, _RET_IP_);
}static inline void raw_write_seqcount_begin(seqcount_t *s)
{s-sequence;smp_wmb();
}写入者在对写之前需要先获得seqlock上的自旋锁lock这说明在写入者之间必须保证互斥操作如果某一写入者成功获得lock。那么需要史新sequence的值以便让其他写入者知道共享数据发生了更新。写入者与写入者之间并不需要sequence。
static inline void write_sequnlock(seqlock_t *sl)
{write_seqcount_end(sl-seqcount);spin_unlock(sl-lock);
}static inline void write_seqcount_end(seqcount_t *s)
{seqcount_release(s-dep_map, 1, _RET_IP_);raw_write_seqcount_end(s);
}static inline void raw_write_seqcount_end(seqcount_t *s)
{smp_wmb();s-sequence;
}主要的工作是释放自旋锁lock至于写入者对sequence的更新主要是用来告诉读取者有数据更新发生所以必须确保sequence的值在写入的前后发生变化。在此基础上sequence提供的另外一个信息是写入过程有没有结束这是用sequence的最低位来完成的如果sequence 0为0表明写入过程己经结束否则表明写入过程正在进行·接下来会在读取者的seqlock作数中看到sequence的这两种用途。 某一写入者可以使用write_tryseqlock来保证在无法获得lock时不让自己进入自旋状态当然也就无法更新数据而直接返回0成功获得锁则返回1
读取者在读取开始前需要先调用read_seqbegin函数该函数主要用来返回读取开始之前的sequence值
static inline unsigned read_seqbegin(const seqlock_t *sl)
{return read_seqcount_begin(sl-seqcount);
}static inline unsigned read_seqcount_begin(const seqcount_t *s)
{seqcount_lockdep_reader_access(s);return raw_read_seqcount_begin(s);
}static inline unsigned raw_read_seqcount_begin(const seqcount_t *s)
{unsigned ret __read_seqcount_begin(s);smp_rmb();return ret;
}s
tatic inline unsigned __read_seqcount_begin(const seqcount_t *s)
{unsigned ret;repeat:ret READ_ONCE(s-sequence);if (unlikely(ret 1)) {cpu_relax();goto repeat;}return ret;
}从函数的实现也可以看出如果当前正好有写入者在进行写操作那么该函数将不停循环直到写过程结束前面曾提到sequence最低位的用途这里正好是其实际使用的地方。另一方面从读取者对写入过程结束的孤环等待可以看出写入者的实际写入操作占用的时间不应太长。 内核还给读取者提供了一个read_seqretry函数与read_seqbegin的返回值一起使用来判定本次的读取操作是否有效
static inline unsigned read_seqretry(const seqlock_t *sl, unsigned start)
{return read_seqcount_retry(sl-seqcount, start);
}static inline int read_seqcount_retry(const seqcount_t *s, unsigned start)
{smp_rmb();return __read_seqcount_retry(s, start);
}static inline int __read_seqcount_retry(const seqcount_t *s, unsigned start)
{return unlikely(s-sequence ! start);
}函数的参数是读取者在读取操作之前调用read_seqbegin获得的初始值。如果本次读取无效读取过程中发生了数据更新那么readq返回1否则返回0。下面分别给出写入者和读取者利用上面介绍的seqlock函数进行数据读/写协调的例子
//定义一个全局的seqlock变量demo_seqlock
DEFINE_SEQLOCK(demo_seqlock);//对于写入者的代码
...
//实际写之前调用write_seqlock获取自旋锁同时更新sequence的值
write_seqlock(demo_seqlock);
//获得自旋锁之后调用do_write进行实际的写入操作
do_write();
//写入结束调用write_sequnlock释放锁
write_sequnlock(demo_seqlock);//对于读取者的代码
unsigned start;
do{//读操作前鲜活的sequence的值start用以在读操作结束时判断数据是否发生更新//注意读操作无需获得锁start read_seqbegin(demo_seqlock);//调用do_read进行实际操作do_read();
}while(read_seqretry(demo_seqlock,start));//如果数据有更新再重新读取如果考虑到中断安全的问题可以使用读取者与写入者的对应版本
void write_seqlock_irq(seqlock_t *sl)
void write_seqlock_bh(seqlock_t *sl)
write_seqlock_irqsave(lock, flags)void read_seqlock_irq(seqlock_t *sl)
void read_seqlock_bh(seqlock_t *sl)
read_seqlock_irqsave(lock, flags前面曾讨论过读取者与写入者自旋锁rwlock,对比这里的seqlock.会发现两者非常相似。不同之处在于seqlock在写的时候只与其他写入者互斥而rwlock在写的时候与读取者和写入者都互斥·因此当要保护的资源很小很简单会很频繁被访问并且写入操作很少发生且必须快速时就可以使用seqlock。