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() 函数都会对序号进行加一操作,所以解锁后,序号的值必定为双数。

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


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

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