`

关于单CPU,多CPU上的原子操作

阅读更多

转载自:http://software.intel.com/zh-cn/blogs/2010/01/14/cpucpu/

 

作者: zdl1016 (1 篇文章) 日期: 一月 14, 2010 在 6:51 下午

所谓原子操作,就是"不可中断的一个或一系列操作" 。

硬件级的原子操作:
在单处理器系统(UniProcessor)中,能够在单条指令中完成的操作都可以认为是" 原子操作",因为中断只能发生于指令之间。这也是某些CPU指令系统中引入了test_and_set、test_and_clear等指令用于临界资源互斥的原因。

在对称多处理器(Symmetric Multi-Processor)结构中就不同了,由于系统中有多个处理器在独立地运行,即使能在单条指令中完成的操作也有可能受到干扰。

在x86 平台上,CPU提供了在指令执行期间对总线加锁的手段。CPU芯片上有一条引线#HLOCK pin,如果汇编语言的程序中在一条指令前面加上前缀"LOCK",经过汇编以后的机器代码就使CPU在执行这条指令的时候把#HLOCK pin的电位拉低,持续到这条指令结束时放开,从而把总线锁住,这样同一总线上别的CPU就暂时不能通过总线访问内存了,保证了这条指令在多处理器环境中 的

原子性。
软件级的原子操作:
软件级的原子操作实现依赖于硬件原子操作的支持。
对于linux而言,内核提供了两组原子操作接口:一组是针对整数进行操作;另一组是针对单独的位进行操作。
2.1. 原子整数操作
针对整数的原子操作只能对atomic_t类型的数据处理。这里没有使用C语言的int类型,主要是因为:

1) 让原子函数只接受atomic_t类型操作数,可以确保原子操作只与这种特殊类型数据一起使用

2) 使用atomic_t类型确保编译器不对相应的值进行访问优化

3) 使用atomic_t类型可以屏蔽不同体系结构上的数据类型的差异。尽管Linux支持的所有机器上的整型数据都是32位,但是使用atomic_t的代 码只能将该类型的数据当作24位来使用。这个限制完全是因为在SPARC体系结构上,原子操作的实现不同于其它体系结构:32位int类型的低8位嵌入了 一个锁,因为SPARC体系结构对原子操作缺乏指令级的支持,所以只能利用该锁来避免对原子类型数据的并发访问。

原子整数操作最常见的用途就是实现计数器。原子整数操作列表在中定义。原子操作通常是内敛函数,往往通过内嵌汇编指令来实现。如果某个函数本来就是原子的,那么它往往会被定义成一个宏。

在编写内核时,操作也简单:

atomic_t use_cnt;

atomic_set(&use_cnt, 2);

atomic_add(4, &use_cnt);

atomic_inc(use_cnt);

2.2. 原子性与顺序性

原子性确保指令执行期间不被打断,要么全部执行,要么根本不执行。而顺序性确保即使两条或多条指令出现在独立的执行线程中,甚至独立的处理器上,它们本该执行的顺序依然要保持。

2.3. 原子位操作

原子位操作定义在文件中。令人感到奇怪的是位操作函数是对普通的内存地址进行操作的。原子位操作在多数情况下是对一个字长的内存访问,因而位号该位于0-31之间(在64位机器上是0-63之间),但是对位号的范围没有限制。

编写内核代码,只要把指向了你希望的数据的指针给操作函数,就可以进行位操作了:

unsigned long word = 0;

set_bit(0, &word); /*第0位被设置*/

set_bit(1, &word); /*第1位被设置*/

clear_bit(1, &word); /*第1位被清空*/

change_bit(0, &word); /*翻转第0位*/

为什么关注原子操作?
1)在确认一个操作是原子的情况下,多线程环境里面,我们可以避免仅仅为保护这个操作在外围加上性能开销昂贵的锁。
2)借助于原子操作,我们可以实现互斥锁。
3)借助于互斥锁,我们可以把一些列操作变为原子操作。

GNU C中x++是原子操作吗?
答案不是。x++由3条指令完成。x++在单CPU下不是原子操作。
对应3条汇编指令
movl x, %eax
addl $1, %eax
movl %eax, x
在vc2005下对应
++x;
004232FA mov eax,dword ptr [x]
004232FD add eax,1
00423300 mov dword ptr [x],eax
仍然是3条指令。
所以++x,x++等都不是原子操作。因其步骤包括了从内存中取x值放入寄存器,加寄存器,把值写入内存三个指令。

如何实现x++的原子性?
在单处理器上,如果执行x++时,禁止多线程调度,就可以实现原子。因为单处理的多线程并发是伪并发。
在多处理器上,需要借助cpu提供的Lock功能。锁总线。读取内存值,修改,写回内存三步期间禁止别的CPU访问总线。同时我估计使用Lock指令锁总线的时候,OS也不会把当前线程调度走了。要是调走了,那就麻烦了。

在多处理器系统中存在潜在问题的原因是:
不使用LOCK指令前缀锁定总线的话,在一次内存访问周期中有可能其他处理器会产生异常或中断,而在异常处理中有可能会修改尚未写入的地址,这样当INC操作完成后会产生无效数据(覆盖了前面的修改)。

spinlock 用于CPU同步, 它的实现是基于CPU锁定数据总线的指令.
当某个CPU锁住数据总线后, 它读一个内存单元(spinlock_t)来判断这个spinlock 是否已经被别的CPU锁住. 如果否, 它写进一个特定值, 表示锁定成功, 然后返回. 如果是, 它会重复以上操作直到成功, 或者spin次数超过一个设定值. 锁定数据总线的指令只能保证一个机器指令内, CPU独占数据总线.
单CPU当然能用spinlock, 但实现上无需锁定数据总线.

spinlock在锁定的时候,如果不成功,不会睡眠,会持续的尝试,单cpu的时候spinlock会让其它process动不了.

分享到:
评论

相关推荐

    多线程程序中的原子操作

    ### 多线程程序中的原子操作 #### 一、引言 在多线程编程中,原子操作是一个至关重要的概念。原子操作指的是不可再分割的操作,即在一个操作执行的过程中不会被其他线程打断。这对于保证多线程程序的正确性和避免...

    windows c++ 原子操作例子

    在Windows平台上,C++编程中使用原子操作(Atomic Operations)是为了保证多线程环境下的数据一致性,避免竞态条件和死锁等问题。原子操作是指在多线程环境下,该操作会被完整无中断地执行,不会被其他线程打断。...

    机组大作业:基于RISC-V架构的45条指令单周期CPU设计

    单周期CPU设计是指CPU中的所有操作在一个时钟周期内完成,这包括取指、解码、执行、访存和写回等步骤。这样的设计减少了时钟周期间的延迟,但可能在处理复杂指令时面临性能瓶颈。在Verilog源码中,我们将看到如何...

    kotlinx.atomicfu,在kotlin中使用原子操作的惯用方法.zip

    8. **最佳实践**: 虽然原子操作可以避免锁,但过度使用可能会导致性能下降,因为CPU仍然需要执行额外的内存屏障指令。因此,在选择使用原子操作时,应确保其确实能带来性能提升,而不是简单地为了避免使用锁。 通过...

    原子操作、信号量、读写信号量和自旋锁的API

    本文将详细介绍其中四种重要的同步机制——原子操作、信号量、读写信号量和自旋锁的API,这些机制对于开发高效稳定的多线程或多处理器系统非常关键。 #### 二、原子操作 **定义**: 原子操作是指一个不可中断的...

    Linux原子加锁中关于SMP的困惑.docx

    在单处理器系统中,原子操作可以简单地通过 CPU 的 LOCK 指令来实现。但是在 SMP 结构中,情况变得更加复杂,因为多个处理器需要共享同一个总线,导致锁的操作可能会受到干扰。 为了解决这个问题,x86 平台上的 CPU...

    并发编程——原子操作CAS.pdf

    在CPU指令级别,现代处理器通常会提供对原子操作的支持,例如通过CMPXCHG指令实现的比较并交换操作。 接下来,文档通过概要的形式,将课程内容划分为几个主要部分,包括:原子操作的实现原理、基本类型原子更新类、...

    atomic_ops原子操作1

    同时,atomic_ops 的实现也需要考虑到多线程和多 CPU 的情况,以确保正确地反映操作的结果。 atomic_ops 是 Linux 内核中的一种非常重要的原子操作机制,它提供了原子 counter、Bit 操作和 spinlock 接口的实现。...

    CPU运算突破瓶颈的希望——原子级晶体管.pdf

    近年来,科研人员在原子级晶体管的研究上取得了突破性进展,为CPU运算性能的提升带来新的曙光。 原子级晶体管,顾名思义,是指其尺寸达到原子级别,这种技术的代表是德州大学科学家们研发的硅烯晶体管。硅烯是一种...

    180个cpu资料(硬件)

    在物理上,CPU是一个封装着多个引脚的芯片,这些引脚与主板上的对应引脚连接,实现CPU与主板之间的通信。 引脚功能是CPU与外部世界交互的关键,每个引脚都有特定的职责。以下是一些常见的CPU引脚功能: 1. **电源...

    linux下原子操作程序源码.zip

    在Linux系统中,原子操作(Atomic Operations)是编程中一种重要的技术,特别是在多线程和并发环境下,确保数据的一致性和完整性。它们提供了一种在不使用锁的情况下更新变量的方法,避免了竞态条件和死锁的问题。在...

    注意CPU超频引起的电子迁移(上).pdf

    CPU内部的结构分析电子迁移,当前的CPU工艺主要是在硅材料上制成晶体管,然后覆盖上二氧化硅绝缘层,最后在绝缘层上制成金属导线。传统的多使用铝材料,但现在已经开始使用钢材料。 在CMOS工艺中,使用最多的是...

    linux_锁_原子_自旋

    在Linux操作系统中,锁、原子操作和自旋锁是内核并发控制的重要机制,用于确保多线程环境下的数据一致性与正确性。这些概念在构建高效、可靠的并发程序时至关重要。 **1. Linux锁** Linux内核中的锁主要用于保护...

    一文读懂原子操作、内存屏障、锁(偏向锁、轻量级锁、重量级锁、自旋锁)、Disruptor、Go Context之上半部分.doc

    本文将从 CPU 硬件设计带来的优势和问题入手,讲解并发编程中的原子操作、锁机制、Disruptor 框架和 Go 语言中的 Context 概念。 一、并发编程概述 并发编程是指在多个任务之间分配执行时间的编程方式,以提高编程...

    C#中使用Interlocked进行原子操作的技巧

    在多线程编程中,确保数据的一致性和正确性至关重要,而原子操作是实现这一目标的关键工具之一。C#中的`System.Threading.Interlocked`类提供了一系列的静态方法,用于执行线程安全的原子操作,避免了锁机制带来的...

    笔记-3、原子操作CAS1

    在Java编程中,原子操作(Atomic Operation)是并发编程中的一种重要概念,它指的是在多线程环境下,某个操作能够不被其他线程中断,保证其完整性。这在并发编程中至关重要,因为线程间的相互干扰可能导致数据不一致...

    关于cpu制作流程,工作原理等

    1. **控制单元(CU)**:控制单元负责解析指令,生成控制信号,协调整个CPU的操作。 2. **算术逻辑单元(ALU)**:ALU执行基本的算术(如加减乘除)和逻辑(如与、或、非)运算。 3. **寄存器**:CPU内部的高速...

    cpu 内存模型和java内存模型

    虽然在硬件层面上,这些操作可能不是原子的,但是JVM会在执行这些操作时加入必要的同步措施,以保证在多线程环境中的原子性。 ### Java锁的性能问题 Java锁的性能问题主要体现在锁的获取和释放上,尤其是当多个...

Global site tag (gtag.js) - Google Analytics