写点什么

【精通内核】Linux 内核 seq(顺序) 锁实现原理与源码解析(四)

  • 2022 年 9 月 13 日
    上海
  • 本文字数:1632 字

    阅读完需:约 5 分钟

前言

📫作者简介小明java问道之路,专注于研究计算机底层/Java/Liunx 内核,就职于大型金融公司后端高级工程师,擅长交易领域的高安全/可用/并发/性能的架构设计📫 

🏆 InfoQ 签约博主、CSDN 专家博主/Java 领域优质创作者/CSDN 内容合伙人、阿里云专家/签约博主、华为云专家、51CTO 专家/TOP 红人 🏆

🔥如果此文还不错的话,还请👍关注、点赞、收藏三连支持👍一下博主~


本文导读

前面的都是比较容易理解的普通互斥锁、自旋锁、信号量、读写锁,本问 seq 锁是在之前的锁实现上做了一些不一样的操作。

一、Linux 内核 seq 锁实现原理

这里的 seq 叫作顺序锁,为什么叫顺序锁?就是对一个 unsigned sequence 变量执行操作。它的特点在哪呢?读和写不会产生阻塞。我们知道在读写锁中,当读锁持有时,写者被阻塞;而这里持有读锁时还能写,也就是读写互不影响。它是如何做到的呢?来看源码实现。

二、Linux 内核 seq 锁源码解析

1、怎么用 seq 锁

do {    seq=read_seqbegin(&foo);         // do Something
}while(read_seqretry(&foo, seq));
复制代码

2、seq 锁源码解析

如果理解了自旋锁、信号量、读写锁后,理解 seq 锁就非常简单,其实就是让读者自己去做判断是否更改了数据,如果更改了那么自己重试去吧。

思考一下,写者之间互斥吗?

答案是互斥的,上述读锁、上锁,未看到自旋锁,所以写者互斥。

对于 seq 锁有什么场景限制吗?

答案是有场景限制。想象一下,如果 seq 锁被用于指针结构,那么会发生什么?A 读者读取了 M 指针并正在操作,然而 B 写者将 M 指针结束了。这对吗?所以 seq 锁不能用于指针类型

// seq 锁结构 typedef struct{    unsigned sequence;	// 声明一个无符号整型大小的变量sequence	    spinlock_t lock;	//自旋锁用于保护上面的sequence,不妨大胆猜测写写互斥	}seqlock_t;
// 这两个宏相当简单,和之前的自旋锁一样,初始化一个 seq 锁结构#define SEQLOCK_UNLOCKED {0, SPIN_LOCK_UNLOCKED }#define seqlock_init(x) do { *(x) =(seqlock_t) SEQLOCK_UNLOCKED; } while (0)
// 持有写锁static inline void write_seqlock(seqlock_t*sl) { // 上自旋锁,这在之前已讲过,这里就不多说了 spin lock(&sl->lock);
// 针对 sequence++,由于初始值为 0,不难看出,当 seq 为奇数时,为写者持有状态,为偶数时为无写状态 ++sl->sequence;
// 全屏障,保证指令有序性 smp_wmb();} // 写顺序释放锁static inline void write_sequnlock(seqlock_t *sl) { smp_wmb(); // 这里会变为偶数 sl->sequence++; spin_unlock(&sl->lock);}
// 无阻塞的尝试获取写锁,其实就是调用spin_trylockstatic inline int write_tryseqlock(seqlock_t *sl) { int ret =spin_trylock(&sl->lock); if(ret) { ++sl->sequence; smp_wmb(); } return ret;}
// 这里并没上锁,而是使用了rmb读屏障。// 这是为何?其实读屏障是为了保证对sequence的读不会被重排序到后面的读操作之后。// 读sequence后返回是为何?很简单,用来比较,想想为何在读者读数据时,// 允许写操作进入执行修改操作?就是在读完并操作完后,读者通过这里读的变量与当前的sequence 做比较,// 如果不相等,是不是就说明了操作完成时有写者更改了状态,那么怎么办?直接重试执行即可static inline unsigned read seqbegin(const seqlockt*sl) { unsigned ret=sl->sequence; smp_rmb(); return ret;}
// 用于判断在readseqbegin期间有没有写者更改了变量,如果之前的iv也就是读者开始读操作时保存的seq值为奇数// 直接重试。如果当前的sequence异或之前保存的seq值不为0的话,即不相等的话,也须重试static inline int read_seqretry(const seqlock_t*sl,unsigned iv) { smp_rmb(); return (iv &1)|(sl->sequence ^iv);}
复制代码

总结

前面的都是比较容易理解的普通互斥锁、自旋锁、信号量、读写锁,本问 seq 锁是在之前的锁实现上做了一些不一样的操作。

发布于: 刚刚阅读数: 4
用户头像

InfoQ签约作者/技术专家/博客专家 2020.03.20 加入

🏆InfoQ签约作者、CSDN专家博主/Java领域优质创作者、阿里云专家/签约博主、华为云专家、51CTO专家/TOP红人 📫就职某大型金融互联网公司高级工程师 👍专注于研究Liunx内核、Java、源码、架构、设计模式、算法

评论

发布
暂无评论
【精通内核】Linux内核seq(顺序)锁实现原理与源码解析(四)_源码_小明Java问道之路_InfoQ写作社区