Linux内核同步 - Seqlock
一、前言
普通的spin lock对待reader和writer是一视同仁,RW spin lock给reader赋予了更高的优先级,那么有没有让writer优先的锁的机制呢?答案就是seqlock。本文主要描述linux kernel 4.0中的seqlock的机制,首先是seqlock的工作原理,如果想浅尝辄止,那么了解了概念性的东东就OK了,也就是第二章了,当然,我还是推荐普通的驱动工程师了解seqlock的API,第三章给出了一个简单的例子,了解了这些,在驱动中(或者在其他内核模块)使用seqlock就可以易如反掌了。细节是魔鬼,概念性的东西需要天才的思考,不是说就代码实现的细节就无足轻重,如果想进入seqlock的内心世界,推荐阅读第四章seqlock的代码实现,这一章和cpu体系结构相关的内容我们选择了ARM64(呵呵~~要跟上时代的步伐)。最后一章是参考资料,如果觉得本文描述不清楚,可以参考这些经典文献,在无数不眠之夜,她们给我心灵的慰籍,也愿能够给读者带来快乐。
二、工作原理
1、overview
seqlock这种锁机制是倾向writer thread,也就是说,除非有其他的writer thread进入了临界区,否则它会长驱直入,无论有多少的reader thread都不能阻挡writer的脚步。writer thread这么霸道,reader肿么办?对于seqlock,reader这一侧需要进行数据访问的过程中检测是否有并发的writer thread操作,如果检测到并发的writer,那么重新read。通过不断的retry,直到reader thread在临界区的时候,没有任何的writer thread插入即可。这样的设计对reader而言不是很公平,特别是如果writer thread负荷比较重的时候,reader thread可能会retry多次,从而导致reader thread这一侧性能的下降。
总结一下seqlock的特点:临界区只允许一个writer thread进入,在没有writer thread的情况下,reader thread可以随意进入,也就是说reader不会阻挡reader。在临界区只有有reader thread的情况下,writer thread可以立刻执行,不会等待。
2、writer thread的操作
对于writer thread,获取seqlock操作如下:
(1)获取锁(例如spin lock),该锁确保临界区只有一个writer进入。
(2)sequence counter加一
释放seqlock操作如下:
(1)释放锁,允许其他writer thread进入临界区。
(2)sequence counter加一(注意:不是减一哦,sequence counter是一个不断累加的counter)
由上面的操作可知,如果临界区没有任何的writer thread,那么sequence counter是偶数(sequence counter初始化为0),如果临界区有一个writer thread(当然,也只能有一个),那么sequence counter是奇数。
3、reader thread的操作如下:
(1)获取sequence counter的值,如果是偶数,可以进入临界区,如果是奇数,那么等待writer离开临界区(sequence counter变成偶数)。进入临界区时候的sequence counter的值我们称之old sequence counter。
(2)进入临界区,读取数据
(3)获取sequence counter的值,如果等于old sequence counter,说明一切OK,否则回到step(1)
4、适用场景。一般而言,seqlock适用于:
(1)read操作比较频繁
(2)write操作较少,但是性能要求高,不希望被reader thread阻挡(之所以要求write操作较少主要是考虑read side的性能)
(3)数据类型比较简单,但是数据的访问又无法利用原子操作来保护。我们举一个简单的例子来描述:假设需要保护的数据是一个链表,header--->A node--->B node--->C node--->null。reader thread遍历链表的过程中,将B node的指针赋给了临时变量x,这时候,中断发生了,reader thread被preempt(注意,对于seqlock,reader并没有禁止抢占)。这样在其他cpu上执行的writer thread有充足的时间释放B node的memory(注意:reader thread中的临时变量x还指向这段内存)。当read thread恢复执行,并通过x这个指针进行内存访问(例如试图通过next找到C node),悲剧发生了……
三、API示例
在kernel中,jiffies_64保存了从系统启动以来的tick数目,对该数据的访问(以及其他jiffies相关数据)需要持有jiffies_lock这个seq lock。
1、reader side代码如下:
u64 get_jiffies_64(void)
{do {
seq = read_seqbegin(&jiffies_lock);
ret = jiffies_64;
} while (read_seqretry(&jiffies_lock, seq));
}
2、writer side代码如下:
static void tick_do_update_jiffies64(ktime_t now)
{
write_seqlock(&jiffies_lock);临界区会修改jiffies_64等相关变量,具体代码略
write_sequnlock(&jiffies_lock);
}
对照上面的代码,任何工程师都可以比着葫芦画瓢,使用seqlock来保护自己的临界区。当然,seqlock的接口API非常丰富,有兴趣的读者可以自行阅读seqlock.h文件。
四、代码实现
1、seq lock的定义
typedef struct {
struct seqcount seqcount;----------sequence counter
spinlock_t lock;
} seqlock_t;
seq lock实际上就是spin lock + sequence counter。
2、write_seqlock/write_sequnlock
static inline void write_seqlock(seqlock_t *sl)
{
spin_lock(&sl->lock);sl->sequence++;
smp_wmb();
}
唯一需要说明的是smp_wmb这个用于SMP场合下的写内存屏障,它确保了编译器以及CPU都不会打乱sequence counter内存访问以及临界区内存访问的顺序(临界区的保护是依赖sequence counter的值,因此不能打乱其顺序)。write_sequnlock非常简单,留给大家自己看吧。
3、read_seqbegin
static inline unsigned read_seqbegin(const seqlock_t *sl)
{
unsigned ret;repeat:
ret = ACCESS_ONCE(sl->sequence); ---进入临界区之前,先要获取sequenc counter的快照
if (unlikely(ret & 1)) { -----如果是奇数,说明有writer thread
cpu_relax();
goto repeat; ----如果有writer,那么先不要进入临界区,不断的polling sequenc counter
}smp_rmb(); ---确保sequenc counter和临界区的内存访问顺序
return ret;
}
如果有writer thread,read_seqbegin函数中会有一个不断polling sequenc counter,直到其变成偶数的过程,在这个过程中,如果不加以控制,那么整体系统的性能会有损失(这里的性能指的是功耗和速度)。因此,在polling过程中,有一个cpu_relax的调用,对于ARM64,其代码是:
static inline void cpu_relax(void)
{
asm volatile("yield" ::: "memory");
}
yield指令用来告知硬件系统,本cpu上执行的指令是polling操作,没有那么急迫,如果有任何的资源冲突,本cpu可以让出控制权。
4、read_seqretry
static inline unsigned read_seqretry(const seqlock_t *sl, unsigned start)
{
smp_rmb();---确保sequenc counter和临界区的内存访问顺序
return unlikely(sl->sequence != start);
}
start参数就是进入临界区时候的sequenc counter的快照,比对当前退出临界区的sequenc counter,如果相等,说明没有writer进入打搅reader thread,那么可以愉快的离开临界区。
还有一个比较有意思的逻辑问题:read_seqbegin为何要进行奇偶判断?把一切都推到read_seqretry中进行判断不可以吗?也就是说,为何read_seqbegin要等到没有writer thread的情况下才进入临界区?其实有writer thread也可以进入,反正在read_seqretry中可以进行奇偶以及相等判断,从而保证逻辑的正确性。当然,这样想也是对的,不过在performance上有欠缺,reader在检测到有writer thread在临界区后,仍然放reader thread进入,可能会导致writer thread的一些额外的开销(cache miss),因此,最好的方法是在read_seqbegin中拦截。
五、参考文献
1、Understanding the Linux Kernel 3rd Edition
2、Linux Kernel Development 3rd Edition
3、Perfbook (https://www.kernel.org/pub/linux/kernel/people/paulmck/perfbook/perfbook.html)
Linux内核同步 - Seqlock的更多相关文章
- [内核同步]浅析Linux内核同步机制
转自:http://blog.csdn.net/fzubbsc/article/details/37736683?utm_source=tuicool&utm_medium=referral ...
- Linux内核同步机制--转发自蜗窝科技
Linux内核同步机制之(一):原子操作 http://www.wowotech.net/linux_kenrel/atomic.html 一.源由 我们的程序逻辑经常遇到这样的操作序列: 1.读一个 ...
- Linux内核同步机制
http://blog.csdn.net/bullbat/article/details/7376424 Linux内核同步控制方法有很多,信号量.锁.原子量.RCU等等,不同的实现方法应用于不同的环 ...
- Linux内核同步
Linux内核剖析 之 内核同步 主要内容 1.内核请求何时以交错(interleave)的方式执行以及交错程度如何. 2.内核所实现的基本同步机制. 3.通常情况下如何使用内核提供的同步机制. 内核 ...
- 浅析Linux内核同步机制
非常早之前就接触过同步这个概念了,可是一直都非常模糊.没有深入地学习了解过,最近有时间了,就花时间研习了一下<linux内核标准教程>和<深入linux设备驱动程序内核机制>这 ...
- Linux内核同步机制之(五):Read Write spin lock【转】
一.为何会有rw spin lock? 在有了强大的spin lock之后,为何还会有rw spin lock呢?无他,仅仅是为了增加内核的并发,从而增加性能而已.spin lock严格的限制只有一个 ...
- Linux内核同步机制之completion【转】
Linux内核同步机制之completion 内核编程中常见的一种模式是,在当前线程之外初始化某个活动,然后等待该活动的结束.这个活动可能是,创建一个新的内核线程或者新的用户空间进程.对一个已有进程的 ...
- Linux内核同步 - Read/Write spin lock
一.为何会有rw spin lock? 在有了强大的spin lock之后,为何还会有rw spin lock呢?无他,仅仅是为了增加内核的并发,从而增加性能而已.spin lock严格的限制只有一个 ...
- Linux内核同步 - spin_lock
一.前言 在linux kernel的实现中,经常会遇到这样的场景:共享数据被中断上下文和进程上下文访问,该如何保护呢?如果只有进程上下文的访问,那么可以考虑使用semaphore或者mutex的锁机 ...
随机推荐
- 动态图片 Movie android-gif-drawable GifView
Movie 类 文档位置:/sdk/docs/reference/android/graphics/Movie.html 官方对这个类连一句介绍都没有,并且所有的方法也没有一行注释,可见多么不受重视! ...
- Golang 中错误与异常需要重新认识
如何进行错误处理,这是一个Go程序员之间,特别是一些新的Go程序员,会经常讨论的问题.讨论到最后往往由于以下代码的多次出现而变成了抱怨. if err != nil { return err } 我们 ...
- Python构造字符串
不断报错 coercing to Unicode : 不要用+连接字符串,要用格式化字符串 None的问题:只能用 or “”的方式来解决了 not all arguments converted d ...
- Nuget添加新项目的问题
为已有的几个项目添加了一个nuget package 后,在解决方法中添加了一个新项目,然后想把这个nuget package添加到这个新建的项目中去,可以此时无法添加. 怎么办那? [解决方 ...
- [Algorithm] Trie data structure
For example we have an array of words: [car, done, try, cat, trie, do] What is the best data structu ...
- 九度OJ 1006 ZOJ问题 (这题測试数据有问题)
题目1006:ZOJ问题 时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:15725 解决:2647 题目描写叙述: 对给定的字符串(仅仅包括'z','o','j'三种字符),推断他能否AC ...
- .net反编译工具ILSpy
下载地址:http://www.fishlee.net/service/softarchive/57
- 在网页浏览器中原生显示PDF文件
在网页中直接显示pdf格式的文件方便阅读.但是如果文件较大加载速度会很慢,另外如果客户端没有安装pdf阅读插件的话,也就看不了了. 这种方式的好处就是不需要转换,直接显示,而且在加载时(高级的浏览器, ...
- android 使用AsyncHttpClient框架上传文件以及使用HttpURLConnection下载文件
AsyncHttpClient开源框架android-async-http还是非常方便的. AsyncHttpClient该类通经常使用在android应用程序中创建异步GET, POST, PUT和 ...
- 解决win10锁屏后无法进入桌面
方法一: TAKEOWN /F "%SystemRoot%\System32\InputMethod\CHS\ChsIME.exe" icacls "%SystemRoo ...