▶ Cinematic fable · Watch on YouTube ▶ 影片版寓言 · 在 YouTube 观看

建康城东的灵台深处,立着一座高逾三丈的千眼铜漏。

铜壶引天泉下滴,浮箭随水势寸寸抬升。台顶正中悬着一卷九尺长的云母粉漆屏,司天令手按算筹,需随时把当值星宿的赤经、赤纬、漏刻与地煞四道细数,飞快誊写在屏风正中。

台下则坐着数十位身穿白麻道袍的誊抄小吏。宫中太极殿、内省更漏司、司天监三处的铜壶滴漏,每隔半刻便要派人赶赴灵台,抄录最新的四数,以正皇城周天之历。

早年间,灵台用的规矩唤作“定音锁”。

司天令每逢提起红狼毫更新屏上数墨,便先拉响铜磬,将一道青铜重栓扣在屏风木架上。铜磬声一响,台下所有抄官哪怕刚落笔半字,也必须硬生生悬手止步,躬立屏外屏息等候。待司天令算清添尽、推开青铜栓、再叩玉磬,众抄官才能一拥而上抢着抄录。

可天泉水急,周天星斗运转不息。灵台的大数几乎每几息便微动一次。

司天令一日之间扣栓上千次。那青铜大栓每扣一次,便如一道无形坚壁横在殿中。三司催历的快马在台阶下排成长龙,抄吏们却只能在屏风前干跺脚。偶有司天令算筹繁复、凝思片刻,青铜栓久久不启,整座灵台便如陷入死寂之渊;而好不容易栓开,数十位抄吏蜂拥推挤,互相遮挡视线、折断毛笔,反而耽误了向太极殿报更的时辰。

后来换了新司天令。新令拆掉了青铜大栓,走到了另一条极端的路子:“撤栓免锁,任尔自抄。”

然而祸事随之更烈。某夜子正,司天令刚改了“赤经”与“漏刻”,笔锋未及落到“赤纬”与“地煞”,一位内省小吏便匆匆抢抄而去。这半新半旧的碎凑之数送入太极殿,更漏司据此错敲了晨钟,文武百官黑夜上朝,在寒霜中乱作一团。

那一年孟冬,司天监老监正亲临灵台。他不添铜栓,也不禁抄笔,只在云母屏正上方悬了一枚朱红小漆牌,又立下三条极精妙的令法:

其一,双数成定,单数成变。 屏顶朱牌常年刻着一串墨数,自初算起为零。凡逢司天令起笔更数,他绝不阻拦台下抄吏,只顺手将朱牌翻动一格,使墨数进一位变成单数(一、三、五……);待四道大数全数落笔妥当,他掷笔之际再顺手一翻,使墨数再进一位归入双数(二、四、六……)。

其二,抄者自抄,两验朱数。 台下白衣抄吏无需等磬,无需叩问,随至随抄。但下笔之前,需先仰头默记屏顶朱数。若见朱数为单,知晓天官正在更数,屏上必有残缺,便立退半步稍候两息;若见朱数为双,则挥毫急录屏上四数。录毕收笔之时,需再次抬头复验朱数。

其三,前契后合,方可成历;若有分毫抵牾,尽废重录。 抄吏收笔抬头,若见此刻朱牌之数与起笔前所见丝毫不差,便可知晓:在这转瞬的抄录之间,司天令从未染指朱牌,自己抄录的四道数墨必为浑然一体、未遭撕裂的真数!反之,若收笔时朱数变了,甚至变作了单数,便说明写官在中途改了屏风,方才所录必有新旧杂糅之虞。抄官无需争辩,亦无需向谁告罪,随手扯碎废纸,退步调息,立即从头再验再抄。

新法一行,灵台格局大变!

数十位白衣抄吏如流云过堂,络绎不绝。九成九的时辰里,司天令只在沉吟推演,屏顶朱牌凝在偶数。抄吏们飞奔而至,昂首一瞥、落墨如飞、再瞥即走,前后不过弹指须臾,再无半点停滞阻噎。

即便偶尔赶上司天令更数,也唯有恰逢那一瞬落笔的二三位小吏需折废一两张草纸、转头重录一次。而那司天令自始至终无需因台下拥簇而迟滞笔意,运筹如神,更无丝毫被抄吏阻拦之患。

自此,建康城更漏分毫不差,周天星轨清明入微。

——到这儿你大概已经认出来了:那座灵台高耸的三丈铜漏与四道微调的大数,正是多核操作系统中需要频繁被读取、偶有写更新的小型核心数据结构(如 Linux 内核中的系统时间 jiffies_64、定时器与网络路由缓存);写屏的司天令与台下不绝的白衣抄吏,正是内核中的写者线程(Writer)与读者线程(Readers);昔年那道令百官寒夜苦候的“定音铜栓”,正是传统的读写锁(Read-Write Spinlock)因争夺总线独占权而导致的严重缓存颠簸与读者排队阻塞;而这枚无需持锁阻拦、凭两验单双以定成败的朱红漆牌,正是高并发低延迟并发控制中至为轻巧精悍的传奇机制——顺序锁(Sequential Lock,简称 SeqLock)。

这是什么

Sequential Lock(顺序锁,简称 SeqLock)是 Linux 内核在 2.6 版本引入的一种为写者极少、读者极多,且保护的数据结构较小的场景量身定制的高性能同步机制。

传统的读写锁(rwlock)虽然允许多个读者并发访问,但读者在进入临界区时必须通过原子指令(如 lock cmpxchg)修改锁的引用计数。当几十甚至上百个 CPU 核心并发读取同一个热点数据(例如系统时钟或路由表)时,这些原子写操作会导致缓存一致性协议(Cache Coherence Protocol)在多核之间疯狂广播失效信号,产生毁灭性的缓存行弹跳(Cache Line Bouncing)与总线争用;更致命的是,若大量读者持续涌入,写者将被无限期饿死。

SeqLock 采用一种极其精巧的乐观并发控制(OCC)+ 序列计数器设计,彻底解除了读者对共享锁的修改依赖:

  1. 序列计数器(Sequence Counter)与单双数约定:
    • SeqLock 内部维护一个整型序列号 sequence,初始为 0(偶数)。
    • 写者流程(独占修改):写者进入临界区前获取自旋锁(排斥其他并发写者),然后将 sequence 加 1 变为奇数。这向全世界宣告:“数据正在被修改,此刻处于不一致的中间状态!”修改完毕后,写者再次将 sequence 加 1 恢复为偶数,并释放自旋锁。写者全程拥有至高无上的优先级。
    • 读者流程(无锁乐观重试):读者完全不需要获取任何锁,不执行任何原子总线修改。它进入临界区前先读取当前的 sequence:
      • 若为奇数,说明写者正在修改,读者循环等待直到其变为偶数;
      • 读到偶数 seq1 后,读者直接读取被保护的数据内容(内存屏障保证读取顺序);
      • 读取完毕后,读者再次读取序列号得到 seq2;
      • 一致性校验:若 seq1 == seq2 且为偶数,说明在读取期间写者从未介入修改,读者获得的数据必然是完全一致的快照,读取宣告成功!
      • 若 seq1 != seq2(或中途检测到奇数),说明读取期间发生了并发写入,当前数据可能存在新旧撕裂(Torn Read),读者直接丢弃本次脏数据,跳回开头重新循环重试。

为什么重要

SeqLock 在现代高性能多核内核架构中扮演着举足轻重的基石角色:

  1. 读路径完全无锁与零总线开销(Wait-Free & Read-Side Zero Bus Contention): 读者在整段执行路径中只对 sequence 进行纯内存读取(Read-Only),无需写回任何计数或原子标记。这意味着 CPU 缓存行(Cache Line)可以以共享状态(Shared 态)在几百个核心之间自由驻留,没有任何缓存行失效惩罚,读扩展性(Read Scalability)达到理论极致。

  2. 写者绝不被读者阻塞或饿死(Writer Never Blocked by Readers): 在传统的互斥锁或公平读写锁中,慢速读者或源源不断的密集读者会卡死写者的更新;但在 SeqLock 中,写者根本不在乎台下有多少个读者正在读取,只需翻转序号便可直接动笔落墨。这对于像操作系统时钟中断(Timer Interrupt)、高精度时间戳(gettimeofday)等必须以极高确定性及时推进的写路径而言,具有不可替代的硬实时价值。

  3. 专为轻量平坦数据打造的极致权衡: 与另一种著名的只读优化机制 RCU(Read-Copy-Update)相比,RCU 依赖指针替换与宽限期(Grace Period)内存回收,适合保护链表、树等复杂动态分配结构;而 SeqLock 专为体积小巧、平铺在内存中的连续结构体(Plain Old Data / Struct)而生。它无需分配新内存节点,无需延迟垃圾回收,仅用 8 字节整型序列号与两行内存屏障,就将系统时钟与简单状态的并发读取开销压榨到了纳秒级别的物理极限。

隐喻对应表

  • 灵台高耸的千眼铜漏与四道微调大数 → 操作系统高频只读的核心平铺数据结构(如 Linux 内核 xtime / jiffies_64)
  • 司天令按筹飞笔更数 → 内核中的独占写者线程(Writer Thread / Timer Interrupt)
  • 络绎不绝的白衣抄吏 → 各 CPU 核心并发发起的时间查询与状态读取者(Concurrent Reader Threads)
  • 早年间阻隔殿门的“定音铜栓” → 传统的排他读写锁(rwlock)引起的总线争用与读者排队阻塞
  • 屏顶朱红小漆牌上的墨数 → 顺序锁的序列号计数器(Sequence Counter)
  • 司天令动笔前进一为单、毕笔再进一归双 → 写者通过加 1 使序列号变奇数标记临界区,写完再加 1 恢复偶数(Write Sequence Increment)
  • 白衣抄吏遇单数则退步稍候 → 读者检测到奇数序列号知晓写入中并主动等待(Reader Spin Wait)
  • 抄吏抄前默记朱数、抄毕再勘朱数 → 顺序锁读者在读数据前后分别执行序列号采样(Read Sequence Sampling)
  • 两度朱数完全吻合方可成历 → 读者比对前后序列号一致即确认无并发写撕裂(Sequence Equality Check)
  • 朱数变动则撕碎草纸从头再录 → 读者检测到序列号不匹配立即抛弃脏读并回滚重试(Optimistic Retry Loop)
  • 抄吏来去如风且司天令运筹无碍 → SeqLock 读者纯只读零缓存颠簸且写者永不被读者阻塞的极致性能(Zero Cache Contention & Writer Priority)

Deep within the imperial observatory east of Jiankang stood a water clock tower rising thirty cubits above the misty terrace.

Fed by cold springs trickling down tiered bronze basins, the clock’s floats lifted their marked arrows inch by inch against the dawn. At the center of the upper pavilion hung a nine-foot mica-powder screen. There, the Master Astrologer held his calculating rods, rapidly inscribing four delicate figures at the center of the scroll: the Right Ascension, Declination, Clepsydra Notch, and Terrestrial Bearing of the ruling constellations.

Below the terrace sat dozens of scribe-monks clad in white linen. Every half-quarter hour, envoys from the Imperial Hall, the Inner Palace Bell Registry, and the Bureau of Astronomers would arrive in haste to transcribe the four numbers and synchronize the realm’s reckoning of time.

In earlier reigns, the observatory was governed by the Bell-and-Bolt Protocol.

Whenever the Master Astrologer took up his red brush to amend the scroll, he would strike a bronze bell and drop a heavy bronze bolt across the screen’s wooden frame. The moment the bell chimed, every scribe below—even those midway through a single stroke—had to arrest their brush mid-air, bowing behind the barrier in anxious silence. Only after the astrologer completed his arithmetic, lifted the heavy bronze bolt, and struck a jade chime could the crowd of scribes surge forward to claim their transcriptions.

Yet the spring water surged without pause, and the heavenly bodies never ceased their orbits. The master’s numbers shifted slightly every few breaths.

A thousand times a day the heavy bolt clattered down. Each drop erected an invisible fortress across the hall. Couriers from the palace formed long, agitated queues down the stone stairs while scribes shuffled their sandals in place. When a calculation proved complex and the astrologer paused in contemplation, the bolt remained fast for long minutes, plunging the entire terrace into agonizing stagnation. When the bolt finally rose, dozens of scribes shoved against one another, blocking each other’s view and snapping bamboo brushes in their haste—delaying the morning chimes of the Great Throne Hall.

A new Master Astrologer took office. Seeking relief, he dismantled the bronze bolt entirely, swinging to the opposite extreme: “No bolts, no locks; transcribe as you please.”

The disaster that followed was swifter still. One midnight, the master revised the Ascension and the Clepsydra Notch, but before his brush could touch the Declination and the Terrestrial Bearing, an overeager palace page swept in and copied the numbers down. This half-baked, fractured ledger was carried straight to the throne hall. Relying on it, the bell-ringers tolled dawn bells in the pitch black of night, sending civilian and military ministers scurrying across frost-covered flagstones in complete disarray.

In the depths of that winter, the Grand Director of the Bureau himself visited the terrace. He brought neither heavy bolts nor decrees banning brushwork. Instead, atop the mica screen he suspended a small vermillion lacquer plaque, establishing three deceptively simple ordinances:

First: Evens signify stillness; odds announce change. The vermillion plaque bore a single black number, initialized to zero. Whenever the Master Astrologer prepared to alter a figure, he barred no one; he merely flipped the plaque by one notch, advancing the number to an odd value (one, three, five…). Once all four calculations were complete, he set down his brush and flipped the plaque once more, advancing it to an even number (two, four, six…).

Second: Read at will; verify the count twice. The white-robed scribes below waited for no bells and sought no permission. They arrived and transcribed at will. But before putting brush to paper, each had to raise his eyes and memorize the number on the plaque. If the count was odd, he knew the master’s brush was active and the figures in flux; he stepped back and waited a breath. If the count was even, he flew his brush across his handheld slip, capturing all four figures in haste. Upon finishing, he raised his eyes once more to re-examine the plaque.

Third: A match seals the ledger; a divergence damns the ink. If the plaque count upon completion matched the initial count to the digit, the scribe knew with mathematical certainty: during the fleeting span of his transcription, the master had never touched the plaque. The four numbers recorded were a pure, untorn, consistent snapshot. Conversely, if the number had changed—or shifted to an odd digit—it proved the writer had intervened mid-stroke. The scribe uttered no protest, shredded the tainted parchment without remorse, breathed, and began the reading anew.

The moment the ordinance took effect, the spirit of the observatory was reborn.

Dozens of white-clad scribes flowed through the pavilion like drifting mist. For ninety-nine percent of the day, the master was deep in contemplation, and the plaque remained motionless on an even digit. Scribes darted in, glanced up, flew their brushes, glanced again, and vanished down the terrace in the span of a single breath—with zero contention, zero waiting, and zero friction.

Even during the rare moments when the master updated the scroll, only the two or three scribes caught in mid-stroke had to discard a sheet and retry. The master himself never suffered hesitation from the press of crowds below; his brush danced across the silk with untroubled priority, completely oblivious to whether one scribe or ten thousand watched his strokes.

From that winter on, the water clocks of Jiankang struck with unblemished precision, and the stars moved across the heavens in flawless cadence.


—By now you have probably recognized the mechanism: the towering water clock and its four constantly shifting figures represent small, frequently queried core data structures in modern operating systems (such as jiffies_64, kernel timers, and network routing state in Linux); the calculating master and the flowing scribes represent concurrent writer and reader threads; the old “Bell-and-Bolt” that froze the court depicts the catastrophic bus contention and reader starvation of traditional Read-Write Spinlocks (rwlocks); and the tiny vermillion plaque flipping between odd and even is one of the most elegant, wait-free concurrency primitives in systems engineering—the Sequential Lock (SeqLock).

What it is

A Sequential Lock (commonly abbreviated as SeqLock) is a specialized synchronization mechanism introduced in the Linux 2.6 kernel, designed specifically for scenarios with very frequent readers, rare writers, and small, compact data structures.

While traditional read-write spinlocks (rwlocks) permit multiple concurrent readers, each reader must execute an atomic instruction (such as lock cmpxchg on x86) to update an internal reader counter. When dozens or hundreds of CPU cores read a single hot memory location simultaneously (such as the system wall clock or routing tables), these atomic writes force cache-coherence protocols to broadcast invalidation storms across inter-socket buses. The resulting cache line bouncing devastates read scalability, while a continuous stream of readers can starve the writer indefinitely.

SeqLock breaks this bottleneck by replacing shared locking with Optimistic Concurrency Control (OCC) driven by a sequence counter:

  1. The Sequence Counter and Parity Invariant:
    • The lock maintains a single integer variable, sequence, initialized to 0 (an even number).
    • The Writer Protocol (Exclusive Mutation): Before entering its critical section, a writer acquires a standard spinlock (serializing against other writers) and increments sequence by 1, making it odd. This broadcasts to the hardware: “A write is underway; data is temporarily inconsistent!” Once the payload update completes, the writer increments sequence again to an even number and unlocks the spinlock. The writer enjoys absolute, unhindered priority.
    • The Reader Protocol (Lockless Optimistic Polling): Readers acquire no locks and execute zero atomic bus instructions. A reader simply samples sequence before reading:
      • If the value is odd, the reader spins briefly until the writer finishes and the value becomes even.
      • Once an even value seq1 is observed, the reader reads the payload data directly (guarded by memory barriers to preserve load ordering).
      • Upon finishing the read, the reader samples the sequence counter a second time, obtaining seq2.
      • The Consistency Check: If seq1 == seq2 (and remained even), the reader is guaranteed that no writer touched the data during the read. The captured snapshot is atomic and coherent.
      • If seq1 != seq2 (or was odd), a writer intervened mid-read, causing a torn read. The reader simply discards the torn payload and loops back to retry.

Why it matters

The SeqLock represents an indispensable engineering milestone in modern high-performance kernel design:

  1. Zero Bus Contention on the Read Path (Wait-Free Scalability): Readers perform strictly read-only loads on the sequence counter and the payload. No cache line transitions to the Modified state, meaning the cache line can remain replicated in the Shared state across hundreds of cores indefinitely. Read scalability scales linearly with core counts, approaching the theoretical hardware limit.

  2. Absolute Writer Priority Without Starvation: In traditional mutual exclusion, a writer can be stalled by slow readers or dense reader storms. In a SeqLock, the writer never yields to readers. It flips the counter, updates memory, and exits. For time-critical paths like operating system timer interrupts (timer IRQs) and high-resolution wall-clock updates (gettimeofday), this deterministic, low-latency execution is crucial.

  3. Minimal Memory Overhead for Plain Old Data (POD): Unlike RCU (Read-Copy-Update), which relies on pointer indirection, dynamic allocations, and deferred grace periods for reclamation, a SeqLock operates directly on contiguous, flat data structures (POD structs). With merely an 8-byte integer counter and two memory barriers, it compresses synchronization overhead for small data down to nanoseconds.

Metaphor mapping

  • The towering water clock and its four shifting figures → Small, contiguous kernel data structures updated periodically (e.g., Linux xtime / jiffies_64)
  • The Master Astrologer updating the mica screen → The exclusive kernel writer thread or timer interrupt (Writer Thread)
  • The endless stream of white-robed scribe-monks → Hundreds of CPU cores issuing concurrent read requests (Reader Threads)
  • The old “Bell-and-Bolt” halting the hall → Heavy bus contention, cache line bouncing, and reader queuing under classic read-write locks (rwlock contention)
  • The vermillion plaque and its inscribed number → The sequence counter inside the SeqLock (Sequence Counter)
  • The master incrementing by one to an odd number before writing and returning to even upon finish → The writer toggling parity to advertise active mutation (Writer Sequence Odd/Even Transition)
  • Scribes pausing upon seeing an odd number → Readers detecting an in-progress write and spinning until completion (Reader Spin Wait)
  • Scribes memorizing the plaque before and after writing → Readers sampling seq1 and seq2 across the read window (Read Sequence Sampling)
  • Identical plaque numbers sealing the ledger → Confirming seq1 == seq2 to ensure a consistent, non-torn snapshot (Sequence Match Validation)
  • Tearing up paper and retrying upon a discrepancy → Discarding torn data and retrying the optimistic read loop (Optimistic Retry on Conflict)
  • Scribes flowing freely while the master’s brush never halts → SeqLock’s zero-bus-contention read scalability and unblockable writer priority (Zero Cache Contention & Writer Priority)
Daily Fables每日寓言 2026-10-09