Linux内核31-Seqlock

简介: Linux内核31-Seqlock

1 什么是seqlock锁?


上一篇文章中,我们已经学习了读/写自旋锁的工作原理和实现方式(基于ARM架构体系)。但是,有一个问题我们不得不考虑,那就是read锁和write锁的优先级问题:它们具有相同的优先级,所以,读操作必须等到写操作完成后才能执行,同样,写操作必须等到读操作完成后才能执行。

Linux2.6内核版本引入了Seqlock锁,与读写自旋锁基本一样,只是对于写操作来说,具有更高的优先级;也就是说,即使现在读操作正在执行,写操作也会被立即执行。这个策略的优点就是,写操作绝不会等待(除非是有其它写操作在占用锁);缺点就是,读操作可能需要读取多次,才能获取正确的备份。


2 seqlock锁实现


seqlock锁的数据结构如下所示,包含两个数据成员lockseqcount。查看代码可知,seqlock锁就是一个自旋锁加上一个序列计数器。

typedef struct {
    struct seqcount seqcount;       // 序列计数器
    spinlock_t lock;
} seqlock_t;

seqlock锁的工作原理是,对于读操作而言,每次读取数据前后,都要读取序列计数器2次,检查这前后两次的值是否一致,一致则认为可以使用锁。相反,如果一个新的写操作开始工作,增加序列计数器的值,隐含地告知读操作刚刚读到的数据不合法,需要重新读取数据。

seqlock_t类型变量初始化的方法有两种:一种是直接赋值SEQLOCK_UNLOCKED,另外一种是调用seqlock_init宏。写操作分别申请锁和释放锁,分别调用write_seqlock()write_sequnlock()。申请锁的过程是,申请seqlock_t数据结构中的自旋锁,并对序列计数器进行加一操作。释放锁的过程是,再一次对序列计数器进行加一操作,并释放掉自旋锁。这样操作的结果就是,写操作过程中,计数器的计数是奇数;没有写操作的时候,计数器是偶数。


3 seqlock锁使用范例


对于读操作来说,大概的代码实现如下所示:

unsigned int seq;
    do {
        seq = read_seqbegin(&seqlock);
        /* ... 临界代码段 ... */
    } while (read_seqretry(&seqlock, seq));

read_seqbegin()获取锁的当前序列号。read_seqretry()判断序列号是否一致,如果seq的值是奇数,则会返回1,也就是条件为真(也就是说,read_seqbegin()函数被调用之后,有写操作更新了数据)。因此,需要重新读取数据。如果seq的值是偶数,则读取数据成功。

值得注意的是,当读操作进入临界代码段时,无需禁止内核抢占。因为,我们允许写操作打断读操作的执行,这也是Seqlock锁写操作优先级高的设计初衷。但是,写操作进入临界代码段时,会自动禁止内核抢占。


4 seqlock锁使用场合


并不是所有的数据结构都能使用seqlock锁保护。因为seqlock锁有自身的缺点:因为写操作的优先级高于读操作,所以,对于写操作负荷比较的重的场合来说就不合适。如果写操作过于频繁,那么对读操作来说极为不公平,可能需要多次读取数据才能成功。所以,使用seqlock锁的场合应该满足下面的条件:

  • 要保护的数据结构不能包含指针,而且这些指针写操作修改,读操作进行引用。因为可能写操作修改了指针,而读操作还会引用之前的指针。
  • 要保护的数据结构必须是特别短小的代码,而且读操作比较频繁,写操作很少且非常快。(这也是读写自旋锁的使用原则)
  • 读操作的临界代码段中的代码不能有副作用(否则,多次读操作可能与单次读取有不同的效果)。

典型应用可以参考linux内核关于系统时间处理的部分。等分析Linux内核关于系统时间的部分时,再详细阐述。


相关文章
|
22天前
|
存储 Linux 数据处理
探索Linux操作系统的内核与文件系统
本文深入探讨了Linux操作系统的核心组件,包括其独特的内核结构和灵活的文件系统。文章首先概述了Linux内核的主要功能和架构,接着详细分析了文件系统的工作原理以及它如何支持数据存储和检索。通过比较不同的文件系统类型,本文旨在为读者提供一个关于如何根据特定需求选择合适文件系统的参考框架。
|
27天前
|
安全 算法 网络协议
探索Linux操作系统的内核管理
【5月更文挑战第31天】本文将深入探讨Linux操作系统的内核管理机制,包括其设计原则、主要组件以及它们如何协同工作以提供高效的系统性能。通过分析Linux内核的关键特性和功能,我们将揭示这一开源操作系统如何在各种计算环境中保持其稳定性和灵活性。
|
28天前
|
机器学习/深度学习 人工智能 负载均衡
深度解析:Linux内核调度策略的演变与优化
【5月更文挑战第30天】 随着计算技术的不断进步,操作系统的性能调优成为了提升计算机系统效率的关键。在众多操作系统中,Linux因其开源和高度可定制性而备受青睐。本文将深入剖析Linux操作系统的内核调度策略,追溯其历史演变过程,并重点探讨近年来为适应多核处理器和实时性要求而产生的调度策略优化。通过分析比较不同的调度算法,如CFS(完全公平调度器)、实时调度类和批处理作业的调度需求,本文旨在为系统管理员和开发者提供对Linux调度机制深层次理解,同时指出未来可能的发展趋势。
|
1月前
|
算法 安全 Linux
探索Linux内核的虚拟内存管理
【5月更文挑战第20天】 在本文中,我们将深入探讨Linux操作系统的核心组成部分之一——虚拟内存管理。通过剖析其关键组件和运作机制,揭示虚拟内存如何提供高效的内存抽象,支持庞大的地址空间,以及实现内存保护和共享。文章将重点讨论分页机制、虚拟内存区域(VMAs)的管理、页面置换算法,并简要分析这些技术是如何支撑起现代操作系统复杂而多变的内存需求的。
|
8天前
|
Linux
查看linux内核版本
在Linux中查看内核版本可使用`uname -r`、`cat /proc/version`、`lsb_release -a`(若安装LSB)、`/etc/*release`或`/etc/*version`文件、`dmesg | grep Linux`、`cat /sys/class/dmi/id/product_name`、`hostnamectl`、`kernrelease`(如果支持)、`rpm -q kernel`(RPM系统)和`dpkg -l linux-image-*`(Debian系统)。
22 4
|
9天前
|
安全 Linux 数据处理
探索Linux的kmod命令:管理内核模块的利器
`kmod`是Linux下管理内核模块的工具,用于加载、卸载和管理模块及其依赖。使用`kmod load`来加载模块,`kmod remove`卸载模块,`kmod list`查看已加载模块,`kmod alias`显示模块别名。注意需有root权限,且要考虑依赖关系和版本兼容性。最佳实践包括备份、查阅文档和使用额外的管理工具。
|
21天前
|
运维 NoSQL Ubuntu
深入理解Linux中的"crash"命令:内核崩溃的调试利器
`crash`是Linux内核崩溃调试工具,用于分析内核崩溃转储文件,提供GDB-like的交互式CLI。通过加载`vmcore`文件和内核映像,管理员可以查看系统状态、调用栈、内存布局等。安装`crash`可使用包管理器,如`apt-get`或`yum/dnf`。尽管有学习曲线且依赖转储文件,但`crash`在系统故障排查中极其重要。
|
28天前
|
Linux
探索Linux操作系统的内核模块
本文将深入探讨Linux操作系统的核心组成部分——内核模块,揭示其背后的工作机制和实现方式。我们将从内核模块的定义开始,逐步解析其加载、卸载以及与操作系统其他部分的交互过程,最后探讨内核模块在系统性能优化中的关键作用。
|
15天前
|
Linux 编译器 C语言
编译Linux内核:基础、重要性和交叉编译方法
Linux内核作为操作系统的心脏,负责管理计算机的硬件资源,同时也是运行所有其他程序的基础。理解如何编译Linux内核对于系统管理员、开发者乃至高级用户来说都是一项极其宝贵的技能。本文将介绍编译Linux内核的基本知识、编译的重要性、具体步骤以及交叉编译的概念。
25 0
|
1月前
|
NoSQL Ubuntu Linux
【操作系统】实验三 编译 Linux 内核
【操作系统】实验三 编译 Linux 内核
58 1