土法炼钢兴趣小组的算法知识备份

【操作系统百科】seqlock 与 seqcount

文章导航

分类入口
os
标签入口
#seqlock#seqcount#retry-read#timekeeper#lockless

目录

xtime / ktime_get() 返回当前时间——这是内核最高频的读路径之一。不能用锁,RCU 也不合适(数据在原地更新)。seqlock 是答案。

一、先看图

sequenceDiagram
    participant W as Writer
    participant R as Reader

    W->>W: write_seqcount_begin<br/>seq++(偶→奇)
    W->>W: 更新数据
    W->>W: write_seqcount_end<br/>seq++(奇→偶)

    R->>R: seq1 = read_seqcount_begin
    R->>R: 读数据(可能不一致)
    R->>R: read_seqcount_retry(seq1)?
    alt seq 变了
        R->>R: 重试
    else seq 没变
        R->>R: 数据有效
    end

二、核心思想

三、seqcount vs seqlock

3.1 seqcount_t

只有序列号,不保护写者之间的互斥:

seqcount_t seq;

// 写者(外部需自己互斥)
write_seqcount_begin(&seq);
data = new_value;
write_seqcount_end(&seq);

// 读者
unsigned s;
do {
    s = read_seqcount_begin(&seq);
    val = data;
} while (read_seqcount_retry(&seq, s));

3.2 seqlock_t

seqcount + spinlock(写者互斥):

seqlock_t lock;

// 写者
write_seqlock(&lock);
data = new_value;
write_sequnlock(&lock);

// 读者
unsigned s;
do {
    s = read_seqbegin(&lock);
    val = data;
} while (read_seqretry(&lock, s));

四、Timekeeper 用法

// kernel/time/timekeeping.c
struct timekeeper {
    seqcount_raw_spinlock_t seq;
    // 时间数据
};

ktime_get() 路径:

do {
    s = read_seqcount_begin(&tk->seq);
    // 读 timekeeper 数据
} while (read_seqcount_retry(&tk->seq, s));

时间更新(tick 中断)只有一个写者 → seqcount 足够。

五、与 RCU 对比

特性 seqlock RCU
数据更新 原地修改 创建新版本
读者失败 重试 无失败
适用场景 小数据、写少读多 指针/链表
内存开销 无额外 旧版本延迟释放

seqlock 适合:小的、可以原地更新的数据(如时间戳、统计计数器)。

六、load tearing 风险

读者可能读到写者写了一半的数据:

// 64 位数据在 32 位架构上
// 读者可能看到 high 32 bit = new, low 32 bit = old

解决:用 READ_ONCE 读每个字段,或保证数据对齐 + 自然宽度。

七、seqcount latch

struct latch_tree {
    seqcount_latch_t seq;
    struct rb_root tree[2];  // 两棵树交替
};

写者更新一棵树 → 切换 seq → 更新另一棵树。读者根据 seq 选择哪棵。

用途:频繁更新的查找结构(如模块地址查找)。

八、typed seqcount(5.10+)

seqcount_spinlock_t     // 关联 spinlock
seqcount_raw_spinlock_t // 关联 raw_spinlock
seqcount_rwlock_t       // 关联 rwlock
seqcount_mutex_t        // 关联 mutex

lockdep 可以跟踪 seqcount 与其保护锁的关系。

九、观察

# seqlock 竞争
bpftrace -e 'kprobe:read_seqcount_begin { @[kstack(3)] = count(); }'
# timekeeper 读重试率
perf stat -e 'sched:sched_stat_runtime' -a sleep 1

十、小结


参考文献

工具


上一篇RCU 下一篇percpu_refcount

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-05-06 · os

【操作系统百科】内核内存调试

内核内存 bug 是最难追的:UAF、OOB、double free、leak 都可能沉默数月。本文讲 KASAN 三种模式、KFENCE 生产采样、kmemleak、SLUB_DEBUG、UBSAN/KCSAN 联动。

2026-05-08 · os

【操作系统百科】VFS 四层抽象

Linux 的一切皆文件靠 VFS 实现——superblock、inode、dentry、file 四层抽象加 ops 表。本文讲 VFS 核心数据结构、dcache、inode cache、RCU lookup,以及文件系统如何插入 VFS。

2026-04-22 · os

操作系统百科

Linux 6.x 视角下的操作系统系列索引:110 篇覆盖调度、虚拟内存、文件系统与 I/O、并发、隔离、可观测性,按主题、阅读路径与关键问题三种入口组织。

2026-05-27 · os

【操作系统百科】用户态分配器:jemalloc vs tcmalloc

jemalloc 与 tcmalloc 都想解决多线程分配器的老问题:锁争抢、碎片、RSS 膨胀与回收抖动。但两者把优化重点放在了不同位置:tcmalloc 更激进地把热路径推到 per-CPU,jemalloc 则把 arena、extent、decay 和 profiling 做成了一套更完整的内存治理工具箱。


By .