linux 内核 --- 整型变量(atomic_t)的原子操作

概述

原子(atomic)本意是“不能被进一步分割的最小粒子”,而原子操作(atomic operation)意为“不可被中断的一个或一系列操作”,可以保证指令以原子的方式运行,即执行过程不被打断。

对一个整数的操作可以用原子函数,避免使用互斥锁、自旋锁等锁机制带来的线程阻塞、锁竞争、死锁、优先级反转、性能损耗等问题。

linux内核提供了一套原子操作的函数,比如

static inline int atomic_dec_and_test(atomic_t *v)

函数功能:减一后判断为0则返回true,否则false。这里的减一和判断是否为0为一个原子操作。

竟态问题

原子操作的提出主要是为了解决程序运行过程中的竞态问题,以程序中一个共享全局变量的自加操作count++为例,处理器完成这样一个操作需要三个步骤:

  1、从内存中读取count变量的值到寄存器中;
  2、将寄存器中的值加1;
  3、将寄存器中的值写回到内存中。
这个操作也称为RMW(Read-Modify-Write)。现在假设存在两个执行线程同时在对count(count初始值假设为0)执行自加操作,预期的结果应当是两个线程串行地完成了count的自增操作,count最终的值为2;但在并发场景下,处理器的实际执行序列却可能会是下面这个情况:

 

 

之所以会产生上面的情况,原因就是RMW操作本身不具备原子性,它可能会被中断打断或者被并行程序产生的数据竞争影响。为了解决这种问题,就需要让RMW操作成为一个原子操作,这需要硬件提供机制来保证。

 

单处理器系统下的原子操作

在单处理器(Uni-Processor)系统中,处理器的执行流程只会受到中断机制的影响,由于中断只能发生于指令之间,因此能够在单条指令中完成的操作都可以认为是“原子操作”。单处理器系统实现原子操作的方式有两种:

  1、提供能完成多步操作的单条指令:这在采用复杂指令集架构的处理器上比较常见,例如x86架构提供的inc指令就可以通过一条指令完成变量的自加操作;
  2、关中断:处理器中断关闭后,就可以不间断地执行一系列指令,等所有操作完成后再打开中断。

多处理器系统下的原子操作

在多处理器(Multi-Processor)系统中,面临的并发问题要严峻很多,由于系统中有多个处理器在独立地运行,即使是一条指令执行期间也会受到其它处理器的干扰,导致指令执行结果错误。在不同的处理器体系结构下,硬件提供的原子操作实现机制会存在区别(硬件提供的原子操作是针对单个整型变量(内存)的操作)。

  • x86架构:  通过对总线加锁保证只允许一个处理器访问
  • ARM架构:通过独占内存
  • RISC-V:   通过 ISA 提供的原子比较和交换指令,可以实现对同一内存地址的真正原子访问

基于原子操作的CAS

Compare And Set(或Compare And Swap),CAS是解决多线程并行情况下使用锁造成性能损耗的一种机制,采用这种无锁的原子操作可以实现线程安全,避免加锁的笨重性。

CAS操作包含三个操作数:内存值(V)、预期原值(A)、新值(B)。

如果内存值与预期原值相等,更新为新值。否则,处理器不做任何操作;最后返回内存位置的值。

CAS是实现自旋锁的基础,CAS 利用硬件提供的原子操作,以达到锁的效果,循环这个指令,直到成功为止。

 

posted @ 2023-02-12 23:18  流水灯  阅读(353)  评论(0编辑  收藏  举报