ILD

Sequence counter
作者:Yuan Jianpeng 邮箱:yuanjp89@163.com
发布时间:2026-5-29 站点:Inside Linux Development

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


Copyright © linuxdev.cc 2017-2024. Some Rights Reserved.