Sequence counter是内核的一个读写同步机制。
内核描述:
a reader-writer consistency mechanism with lockless readers (read-only retry loops),
and no writer starvation.
用在几乎不写的(比如系统时间), 且读者愿意重试的场景。
它用来保护一个数据集(data set)的一致性。
原理:
维护一个序列号。初始化为偶数。
当写的时候,增加序列号,变为奇数,写完再增加序列号,又变成偶数。
读者判断序列号是奇数,并且没有变化,则认为数据是一致性的。如果不是一致性,就重试。
它要求writer必须序列化且不可抢占:
Write side critical sections must be serialized and non-preemptible.
如果reader在hardirq或者softirq运行。则writer必须禁止interrupt或者bottom halves。
原因:
1 不能同时写,否则数据集显然不是一致性的。
2 不能被抢占,如果被reader抢占,那么reader就是一直重试,直到时间片耗尽
上面的特点,使它衍生出3个版本:
| seqcount_t | 原始版本,writer需要自己就保护。 |
seqcount_spinlock_t seqcount_raw_spinlock_t seqcount_rwlock_t seqcount_mutex_t seqcount_ww_mutex_t | lockdep版本,writer也需要自己保护, 但是初始化的时候提供一个lock lockdep可以帮助检查writer对应的锁是否加上了。 |
| seqlock_t | 带lock的版本。writer会自动保护。 |
一个例子,证明seqcount_t的用法:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/seqlock.h>
#include <linux/kthread.h>
static int data0, data1;
static seqcount_t d_seq;
static struct task_struct *reader;
static struct task_struct *writer;
static int reader_fn(void *data)
{
while (!kthread_should_stop()) {
long seq;
int d0, d1;
do {
seq = read_seqcount_begin(&d_seq);
d0 = data0;
d1 = data1;
} while (read_seqcount_retry(&d_seq, seq));
if (d0 != d1)
printk("data0 %d data1 %d\n", d0, d1);
}
return 0;
}
static int writer_fn(void *data)
{
while (!kthread_should_stop()) {
preempt_disable();
write_seqcount_begin(&d_seq);
data0++;
data1++;
write_seqcount_end(&d_seq);
preempt_enable();
}
return 0;
}
static int __init test_init(void)
{
seqcount_init(&d_seq);
reader = kthread_run(reader_fn, NULL, "seqcount reader");
writer = kthread_run(writer_fn, NULL, "seqcount reader");
return 0;
}
static void __exit test_exit(void)
{
kthread_stop(reader);
kthread_stop(writer);
}
module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jianpeng Yuan");
MODULE_DESCRIPTION("test module");如果注释掉write_seqcount_begin/end,那么将打印data0和data1不一致。
[22891.744664] data0 214043467 data1 214043466
注意
write_seqcount_begin()对于有锁关联,且会开启抢占的锁,会自动调用preempt_disable
类型定义:include/linux/seqlock_types.h
接口定义:include/linux/seqlock.h
内核文档:Documentation/locking/seqlock.rst