Redian新闻
>
一文读懂|内核顺序锁

一文读懂|内核顺序锁

科技

Linux 内核有非常多的锁机制,如:自旋锁、读写锁、信号量和 RCU 锁等。本文介绍一种和读写锁比较相似的锁机制:顺序锁(seqlock)。

顺序锁与读写锁一样,都是针对多读少写且快速处理的锁机制。而顺序锁和读写锁的区别就在于:读写锁的读锁会阻塞写锁,而顺序锁的读锁不会阻塞写锁。

读锁原理

为了让读锁不阻塞写锁,读锁并不会真正进行上锁操作。那么读锁是如何避免在读取临界区数据时,数据被其他进程修改了?

为了解决这个问题,顺序锁使用了一种类似于版本号的机制:序号。序号是一个只增不减的计数器,可以从顺序锁对象的定义看出,如下代码所示:

typedef struct {
     struct seqcount seqcount; // 序号
     spinlock_t lock;          // 自旋锁,写锁上锁时使用
seqlock_t;

在读取临界区数据前,首先需要调用 read_seqbegin() 函数来获取读锁,read_seqbegin() 函数的核心逻辑是读取顺序锁的序号。代码如下所示:

static inline unsigned read_seqbegin(const seqlock_t *sl)
{
    unsigned ret;

repeat:
    // 读取顺序锁的序号
    ret = sl->sequence;

    // 如果序号是单数,需要重新获取
    if (unlikely(ret & 1)) {
        ...
        goto repeat;
    }
    ...
    return ret;
}

从上面的代码可以看出,read_seqbegin() 函数只获取顺序锁的序号,并不会进行上锁操作,所以读锁并不会阻塞写锁。

注意:序号是单数时需要重新获取的原因,会在分析写锁实现原理时说明。

既然读锁并不会进行上锁操作,如果在读取临界区数据时,数据被修改了怎么办呢?答案就是:在退出临界区时,比较一下当前顺序锁的序号跟之前读取的序号是否一致。如果一致表示数据没有被修改,否则说明数据已经被修改。如果数据被修改了,那么需要重新读取临界区的数据。

比较序号是否一致可以使用 read_seqretry() 函数,所以读锁的正确用法如下代码所示:

do {
    // 获取顺序锁序号
    unsigned seq = read_seqbegin(&seqlock);
    // 读取临界区数据
    ...
while (read_seqretry(&seqlock, seq)); // 对比序号是否一致?

read_seqretry() 函数的实现非常简单,如下所示:

static inline unsigned 
read_seqretry(const seqlock_t *sl, unsigned start)
{
    ...
    return sl->sequence != start;
}

从上面代码可以看出,read_seqretry() 函数只是简单比较当前序号与之前读取到的序号是否一致。

写锁原理

从上面的分析可知,读锁是通过对比前后序号是否一致来判断数据是否被修改的。那么序号在什么时候被修改呢?答案就是:获取写锁时。

获取写锁是通过 write_seqlock() 函数来实现的,其实现也比较简单,代码如下所示:

static inline void write_seqlock(seqlock_t *sl)
{
    spin_lock(&sl->lock);

    sl->sequence++;
    ...
}

write_seqlock() 函数首先会获取自旋锁(所以写锁与写锁之间是互斥的),然后对序号进行加一操作。所以,在修改临界区数据前,写锁先会增加序号的值,这样就会导致读锁前后两次获取的序号不一致。我们可以用下图来说明这种情况:

seqlock原理

可以看出,当在读临界区前后获取的序号值不一致时,就表示数据已经被修改,这时就需要重新读取被修改后的数据。

写锁解锁也很简单,代码如下:

static inline void write_sequnlock(seqlock_t *sl)
{
  ...
 s->sequence++;
 spin_unlock(&sl->lock);
}

解锁也需要对序号进行加一操作,然后释放自旋锁。

由于 write_seqlock() 函数与 write_sequnlock() 函数都会对序号进行加一操作,所以解锁后,序号的值必定为双数。

我们在分析读锁时看到,如果序号是单数时会重新获取序号,直到序号为双数为止。这是因为序号单数时,表示正在更新数据。此时读取临界区的值是没有意义的,所以需要等到更新完毕再读取。


微信扫码关注该文公众号作者

戳这里提交新闻线索和高质量文章给我们。
相关阅读
[速领]加拿大银行发福利了!$400刀现金+免月费,一文读懂如何领钱!一文读懂欧盟“碳边境调节机制”和 “绿色协议产业计划”新动向《知道》&《白首》五类家族性高胆固醇血症的治疗方法,一文读懂一文读懂「青少年边缘型人格障碍」的识别与干预一文读懂,这波房贷利率调整你能省多少钱一文读懂|zRAM 内存压缩机制中国实施出口管制的镓和锗,一文读懂!2023年7月5日,早8:30,清晨球赛练习有感一文读懂“马扎笼斗”:马斯克一直耍嘴皮子,扎克伯格已经心灰意冷大国重建:一文读懂北京中轴线一文读懂|Linux 虚拟文件系统(VFS)长寿的弊端一文读懂ESG投资的那点事儿小刀說古第四回:慷慨歌燕市 從容作楚囚 引刀成一快 不負少年頭一文读懂依托咪酯诱发肌颤有人月供可少还840元!如何申请下调首套房贷利率?能省多少钱?当时所在城市利率下限是多少?一文读懂一文读懂OSS、NAS、EBS有什么区别?往事随风-阎明复的侄女一文读懂保税仓库最新规定一文读懂 OpenAI 创始人的「世界币」​一文读懂,7 种腹部盆腔血管压迫综合征的 CT 表现必看!一文读懂澳大利亚移民政策今年7月1日后的新变化!一文读懂华为鸿蒙 4:五大功能更新、接入大模型、打响生态战役一文读懂|RDMA原理一图读懂|吉林银行2023半年度报告一文读懂,新移民首次登陆美国详细指南以及移民方式一文读懂Biotech出海关键 | 劲方医药:变化的BD模式,不变的成功一文读懂领先的餐饮连锁企业都在用什么软件|36氪企服点评在用趋势一文读懂「日本核污染水」各类问题一文读懂OpenAI创始人的“世界币”一文读懂 QUIC 协议:更快、更稳、更高效的网络通信一文读懂ESG那些事儿转学UC申请时间线,求真带你一文读懂一文读懂美团直播基本盘
logo
联系我们隐私协议©2024 redian.news
Redian新闻
Redian.news刊载任何文章,不代表同意其说法或描述,仅为提供更多信息,也不构成任何建议。文章信息的合法性及真实性由其作者负责,与Redian.news及其运营公司无关。欢迎投稿,如发现稿件侵权,或作者不愿在本网发表文章,请版权拥有者通知本网处理。