Linux内核29-原子操作

简介: Linux内核29-原子操作

1 引言


汇编指令读写内存变量的过程我们称为read-modify-write,简称为RMW操作。也就是说,它们读写一个内存区域两次,第一次读取旧值,第二次写入新值。

假设有两个不同的内核控制路径运行在两个CPU上,同时尝试RMW操作相同的内存区域且执行的是非原子操作。起初,两个CPU尝试读取相同位置,但是内存仲裁器(促使串行访问RAM的电路)确定一个可以访问,让另一个等待。但是,当第一个读操作完成,延时的CPU也会读取相同的旧值。但是等到两个CPU都往这个内存区域写入新值的时候,还是由内存仲裁器决定谁先访问,然后写操作都会成功。但是,最终的结果却是最后写入的值,先写入的值会被覆盖掉。

防止RMW操作造成的竞态条件最简单的方式就是保证这样的指令操作是原子的,也就是这个指令的执行过程不能被打断。这就是原子操作的由来。


2 X86体系架构


2.1 X86原子指令

让我们看一下X86的汇编指令有哪些是原子的:

  • 进行零或一对齐内存访问的汇编指令是原子的。
  • RMW操作汇编指令(比如incdec),如果在read之后,write之前内存总线没有被其它CPU抢占,那么这些指令就是原子的。
  • 所以,基于上一点,RMW操作汇编指令前缀lock(0xf0)就称为原子操作指令。当控制单元检测到这个前缀,它会锁住内存总线,直到指令完成。
  • 带有前缀rep(0xf2、0xf3,强迫控制单元重复指令多次)的汇编指令就不是原子的。

通过上面的描述可知,X86体系架构本身有一些指令就是原子指令。对于RMW操作指令(比如incdec),本身不是原子指令,但是可以通过在指令前面,使用前缀lock指令锁住内存总线,阻止在写内存时,其它CPU抢占,从而实现原子操作。


2.2 ARM原子指令


但是,ARM体系架构中不存在lock指令,所以它在原子指令的实现上是不一样的。ARMv6之前的版本,因为不支持多核,所以只要关闭中断即可;而ARMv6及以后的版本,支持多核系统,只关闭中断是不可以实现原子指令的。于是,该版本引入了新的独占指令ldrexstrex,通过这两个指令实现原子操作。比如,下面以原子加法为例,代码如下:

static inline void atomic_add(int i, atomic_t *v)
{
    unsigned long tmp;
    int result;
    __asm__ __volatile__("@ atomic_add\n"
    "1: ldrex %0, [%3]\n"       // ---------- (0)
    " add %0, %0, %4\n"         // ---------- (1)
    " strex %1, %0, [%3]\n"     // ---------- (2)
    " teq %1, #0\n"             // ---------- (3)
    " bne 1b"                   // ---------- (4)
    : "=&r" (result), "=&r" (tmp), "+Qo" (v->counter)
    : "r" (&v->counter), "Ir" (i)
    : "cc");
}

代码解析:

  • (0)从v->counter地址处取出其值,将其存入result;
  • (1)计算result=result+i;
  • (2)将result的结果存入v->counter地址处,这一步操作是否成功的结果写入到tmp临时变量中;
  • (3)判断tmp是否等于0;
  • (4)第(3)结果如果等于0,则成功;如果不等于0,则跳转到标签1处继续执行,直到成功。

所以说,X86这种锁内存总线的方式简单好用,但是毕竟牺牲了性能;而ARM这种独占指令则更为高效,只不过实现上更为复杂一点。


3 Linux原子操作


但是,我们在编写完C代码后,编译器不能保证给你使用原子指令进行替代。因此,Linux内核提供了atomic_t类型变量并提供了相关的操作函数和宏(如表5-4所示)。

表5-4 Linux中的原子操作

函数 描述
atomic_read(v) 返回*v
atomic_set(v,i) *v=i
atomic_add(i,v) *v+i
atomic_sub(i,v) *v-i
atomic_sub_and_test(i, v) 如果*v-i = 0
返回1;否则0
atomic_inc(v) *v+1
atomic_dec(v) *v-1
atomic_dec_and_test(v) 如果*v-1 = 0
返回1;否则0
atomic_inc_and_test(v) 如果*v+1 = 0
返回1;否则0
atomic_add_negative(i, v) 如果*v+i < 0
返回1;否则0
atomic_inc_return(v) 返回*v-1
atomic_dec_return(v) 返回*v+i
atomic_add_return(i, v) 返回*v-i


相关文章
|
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权限,且要考虑依赖关系和版本兼容性。最佳实践包括备份、查阅文档和使用额外的管理工具。
|
15天前
|
Linux 编译器 C语言
编译Linux内核:基础、重要性和交叉编译方法
Linux内核作为操作系统的心脏,负责管理计算机的硬件资源,同时也是运行所有其他程序的基础。理解如何编译Linux内核对于系统管理员、开发者乃至高级用户来说都是一项极其宝贵的技能。本文将介绍编译Linux内核的基本知识、编译的重要性、具体步骤以及交叉编译的概念。
25 0
|
21天前
|
运维 NoSQL Ubuntu
深入理解Linux中的"crash"命令:内核崩溃的调试利器
`crash`是Linux内核崩溃调试工具,用于分析内核崩溃转储文件,提供GDB-like的交互式CLI。通过加载`vmcore`文件和内核映像,管理员可以查看系统状态、调用栈、内存布局等。安装`crash`可使用包管理器,如`apt-get`或`yum/dnf`。尽管有学习曲线且依赖转储文件,但`crash`在系统故障排查中极其重要。
|
22天前
|
存储 Linux 数据处理
探索Linux操作系统的内核与文件系统
本文深入探讨了Linux操作系统的核心组件,包括其独特的内核结构和灵活的文件系统。文章首先概述了Linux内核的主要功能和架构,接着详细分析了文件系统的工作原理以及它如何支持数据存储和检索。通过比较不同的文件系统类型,本文旨在为读者提供一个关于如何根据特定需求选择合适文件系统的参考框架。
|
27天前
|
安全 算法 网络协议
探索Linux操作系统的内核管理
【5月更文挑战第31天】本文将深入探讨Linux操作系统的内核管理机制,包括其设计原则、主要组件以及它们如何协同工作以提供高效的系统性能。通过分析Linux内核的关键特性和功能,我们将揭示这一开源操作系统如何在各种计算环境中保持其稳定性和灵活性。
|
28天前
|
机器学习/深度学习 人工智能 负载均衡
深度解析:Linux内核调度策略的演变与优化
【5月更文挑战第30天】 随着计算技术的不断进步,操作系统的性能调优成为了提升计算机系统效率的关键。在众多操作系统中,Linux因其开源和高度可定制性而备受青睐。本文将深入剖析Linux操作系统的内核调度策略,追溯其历史演变过程,并重点探讨近年来为适应多核处理器和实时性要求而产生的调度策略优化。通过分析比较不同的调度算法,如CFS(完全公平调度器)、实时调度类和批处理作业的调度需求,本文旨在为系统管理员和开发者提供对Linux调度机制深层次理解,同时指出未来可能的发展趋势。
|
28天前
|
Linux
探索Linux操作系统的内核模块
本文将深入探讨Linux操作系统的核心组成部分——内核模块,揭示其背后的工作机制和实现方式。我们将从内核模块的定义开始,逐步解析其加载、卸载以及与操作系统其他部分的交互过程,最后探讨内核模块在系统性能优化中的关键作用。
|
29天前
|
缓存 算法 安全
探索Linux内核的虚拟内存管理
【5月更文挑战第29天】 在现代操作系统中,虚拟内存是支持多任务处理和内存保护的关键组件。本文深入分析了Linux操作系统中的虚拟内存管理机制,包括其地址空间布局、分页系统以及内存分配策略。我们将探讨虚拟内存如何允许多个进程独立地访问它们自己的地址空间,同时由操作系统管理物理内存资源。此外,文章还将涉及虚拟内存所带来的性能影响及其优化方法。
|
29天前
|
算法 Linux 调度
深度解析:Linux内核的进程调度机制
【5月更文挑战第29天】 在现代操作系统中,尤其是类Unix系统如Linux中,进程调度机制是保证多任务高效运行的核心。本文将深入探讨Linux操作系统内核的进程调度器——负责管理CPU资源分配的关键组件。我们会详细分析其调度策略、调度器的演进及其在多核处理器环境下的表现。通过剖析进程调度器的工作原理和设计哲学,旨在为读者提供一个清晰的视角来理解这一复杂的系统功能。
26 0