`
dingdingji
  • 浏览: 6240 次
  • 性别: Icon_minigender_1
  • 来自: 北京
最近访客 更多访客>>
社区版块
存档分类
最新评论

内存屏障

    博客分类:
  • Java
 
阅读更多

当你看到“内存屏障”四个字的时候,你的第一反应是什么?寄存器里取出了错误的值?ifence,sfence之类的指令?还是诸如volatile之类的关键字?好吧,我第一次看到这四个字的时候,脑子里浮现出的是魔兽争霸里绿油油的铺满苔藓的岩石屏障- -#,并且,当我搞明白内存屏障具体是什么,而且自认为对其很熟悉之后,我的第一反应依然是那几块绿油油的石头,而且很想上去A一把!

言归正传,先解释下什么是内存屏障。内存屏障是指“由于编译器的优化和缓存的使用,导致对内存的写入操作不能及时的反应出来,也就是说当完成对内存的写入操作之后,读取出来的可能是旧的内容”(摘自《独辟蹊径品内核》)。(这里概念貌似不是很准确,正确的定义:为了防止编译器和硬件的不正确优化,使得对存储器的访问顺序(其实就是变量)和书写程序时的访问顺序不一致而提出的一种解决办法。 它不是一种错误的现象,而是一种对错误现象提出的解决方发----欢迎指正!!)

概念就是概念,生硬的东西,懂的人能从中悟出点什么,不懂的人还是一头雾水。不要着急,我们先给内存屏障分下类,然后挨个来研究一番,等看完这篇文章,再回来读读概念,你就懂了!

内存屏障的分类:

  1. 编译器引起的内存屏障
  2. 缓存引起的内存屏障
  3. 乱序执行引起的内存屏障

1、编译器引起的内存屏障:

我们都知道,从寄存器里面取一个数要比从内存中取快的多,所以有时候编译器为了编译出优化度更高的程序,就会把一些常用变量放到寄存器中,下次使用该变量的时候就直接从寄存器中取,而不再访问内存,这就出现了问题,当其他线程把内存中的值改变了怎么办?也许你会想,编译器怎么会那么笨,犯这种低级错误呢!是的,编译器没你想象的那么聪明!让我们看下面的代码:(代码摘自《独辟蹊径品内核》

int flag=0;
 
void wait(){
    while ( flag == 0 )
        sleep(1000);
    ......
}
 
void wakeup(){
    flag=1;
}

这段代码表示一个线程在循环等待另一个线程修改flag。 Gcc等编译器在编译的时候发现,sleep()不会修改flag的值,所以,为了提高效率,它就会把某个寄存器分配给flag,于是编译后就生成了这样的伪汇编代码:

void wait(){
    movl  flag, %eax;
 
    while ( %eax == 0)
        sleep(1000);
}

这时,当wakeup函数修改了flag的值,wait函数还在傻乎乎的读寄存器的值而不知道其实flag已经改变了,线程就会死循环下去。由此可见,编译器的优化带来了相反的效果!

但是,你又不能说是让编译器放弃这种优化,因为在很多场合下,这种优化带来的性能是十分可观的!那我们该怎么办呢?有没有什么办法可以避免这种情况?答案必须是肯定的,我们可以使用关键字volatile来避免这种情况

volatile int flag = 0;

这样,我们就能避免编译器把某个寄存器分配给flag了。

好,上面所描述这些,就叫做“编译器优化引起的内存屏障”,是不是懂了点什么?再回去看看概念?

2、缓存引起的内存屏障

好,既然寄存器能够引起这样的问题,那么缓存呢?我们都知道,CPU会把数据取到一个叫做cache的地方,然后下次取的时候直接访问cache,写入的时候,也先将值写入cache。

那么,先让我们考虑,在单核的情况下会不会出现问题呢?先想一下,单核情况下,除了CPU还会有什么会修改内存?对了,是外部设备的DMA!那么,DMA修改内存,会不会引起内存屏障的问题呢?答案是,在现在的体系结构中,不会。

当外部设备的DMA操作结束的时候,会有一种机制保证CPU知道他对应的缓存行已经失效了;而当CPU发动DMA操作时,在想外部设备发送启动命令前,需要把对应cache中的内容写回内存。在大多数RISC的架构中,这种机制是通过一写个特殊指令来实现的。在X86上,采用一种叫做总线监测技术的方法来实现。就是CPU和外部设备访问内存的时候都需要经过总线的仲裁,有一个专门的硬件模块用于记录cache中的内存区域,当外部设备对内存写入的时候,就通过这个硬件来判断下改内存区域是否在cache中,然后再进行相应的操作。

那么,什么时候才能产生cache引起的内存屏障呢?多CPU? 是的,在多CPU的系统里面,每个CPU都有自己的cache,当同一个内存区域同时存在于两个CPU的cache中时,CPU1改变了自己cache中的值,但是CPU2却仍然在自己的cache中读取那个旧值,这种结果是不是很杯具呢?因为没有访存操作,总线也是没有办法监测的,这时候怎么办?

对阿,怎么办呢?我们需要在CPU2读取操作之前使自己的cache失效,x86下,很多指令能做到这点,如lock前缀的指令,cpuid, iret等。内核中使用了一些函数来完成这个功能:mb(), rmb(), wmb()。用的也是以上那些指令,感兴趣可以去看下内核代码。

3、乱序执行引起的内存屏障:

我们都知道,超标量处理器越来越流行,连龙芯都是四发射的。超标量实际上就是一个CPU拥有多条独立的流水线,一次可以发射多条指令,因此,很多允许指令的乱序执行,具体怎么个乱序方法,可以去看体系结构方面的书,这里只说内存屏障。

指令乱序执行了,就会出现问题,假设指令1给某个内存赋值,指令2从该内存取值用来运算。如果他们两个颠倒了,指令2先从内存中取值运算,是不是就错了?

对于这种情况,x86上专门提供了lfence,sfence,和mfence 指令来停止流水线:

lfence:停止相关流水线,知道lfence之前对内存进行的读取操作指令全部完成

sfence:停止相关流水线,知道lfence之前对内存进行的写入操作指令全部完成

mfence:停止相关流水线,知道lfence之前对内存进行的读写操作指令全部完成

分享到:
评论

相关推荐

    内存屏障原理解析

    内存屏障是一种在计算机科学中用来控制指令执行顺序和内存读写的同步机制。由于现代计算机系统的CPU处理速度远超过内存访问速度,为了提高性能,CPU会采用缓存(Cache)技术,而这种技术往往会导致内存操作的重新...

    内存屏障机制

    ### 内存屏障机制及其在Linux Kernel中的应用 #### 一、内存屏障基本概念 内存屏障(Memory Barrier),又称内存栅栏或内存围栏,是一种用于控制处理器内部内存操作顺序的机制。它确保某些类型的内存操作按指定...

    内存屏障访问顺序

    ### 内存屏障访问顺序详解 #### 引言 随着技术的发展与摩尔定律的推进,处理器的速度不断提升,而内存访问速度却未能跟上这一步伐。这种差异导致内存操作成为现代处理器性能瓶颈之一。为了缓解这个问题,现代...

    内核同步机制-优化屏障和内存屏障

    优化屏障和内存屏障是内核同步的两种重要手段,用于防止编译器和处理器的优化导致的数据乱序问题。 优化屏障主要用于阻止编译器对源代码指令的重排序。在Linux内核中,`barrier()`宏就是一个典型的优化屏障实现,它...

    LINUX内核内存屏障

    ### Linux内核内存屏障知识点详解 #### 一、引言 在现代计算机系统尤其是多处理器系统(SMP)中,为了提高性能,处理器通常会采用缓存机制来减少访问主存的时间延迟。然而,这种机制可能导致不同处理器之间数据的...

    linux 内核内存屏障

    ### Linux内核内存屏障知识点详解 #### 一、内存访问抽象模型 在现代计算机系统中,内存访问操作可能会出现乱序执行的现象。这种现象主要来源于CPU的指令流水线技术,该技术通过并行处理指令的不同阶段来提高...

    java内存屏障与JVM并发详解实用.pdf

    Java内存屏障与JVM并发详解实用 Java内存屏障是java并发编程中的一种机制,用于确保多线程程序的正确执行。它通过强制处理器顺序执行内存操作,从而避免了内存屏障带来的问题。在本文中,我们将深入探讨Java内存...

    内存屏障浅析

    内存屏障浅析,多线程编程,由于编译器的优化和缓存的使用,导致对内存的写入操作不能及时的反应出来,也就是说当完成对内存的写入操作之后,读取出来的可能是旧的内容

    中国Linux内核开发者大会十周年演讲稿(中兴通讯谢宝友)-Linux内存屏障

    在大会十周年的庆典上,中兴通讯的谢宝友带来了关于“Linux内存屏障”的主题演讲,这是一个深入讨论并发编程和多处理器系统中的关键技术的话题。 内存屏障,也称为内存栅栏或内存顺序约束,是CPU架构和操作系统中的...

    Linux内存屏障1

    Linux内存屏障是并行编程中一个至关重要的概念,它涉及到多核CPU的缓存一致性问题。在现代计算机系统中,由于CPU的运算速度远超内存访问速度,CPU通常会使用缓存来加速数据读写。然而,当多个CPU核心同时访问同一块...

    JVM内存模型-重排序&内存屏障 1

    在这个模型中,内存屏障(Memory Barrier)和重排序(Reordering)是两个关键概念,它们对并发编程的正确性和性能有着重要影响。 **重排序** 重排序是指编译器和处理器为了优化程序性能,可能会改变程序执行顺序的...

    乱序执行和内存屏障.pdf

    乱序执行和内存屏障 乱序执行是高级处理器中的一种技术,为了提高内部逻辑元件的利用率以提高运行速度,处理器通常会采用多指令发射、乱序执行等各种措施。乱序执行可以使处理器在一个指令周期内并发执行多条指令,...

    JVM内存模型-内存结构-内存屏障

    主要为大家讲解JVM内存模型|内存结构|内存屏障,他们的概念,有什么关联以及各种的功能

    Linux内存屏障

    Linux内存屏障是并行编程领域的重要概念,它关注的是如何在多处理器系统中保持内存操作的顺序性和一致性。在进行并行编程时,尤其是在多核CPU环境下,内存访问顺序和一致性问题尤为突出。为了解决这些问题,处理器...

    Memory Barriers: a Hardware View for Software Hackers 讲解内存屏障的好论文,推荐!

    内存屏障是一种在多处理器系统中被广泛使用的同步机制,它确保了内存操作的顺序性,对于保证多核处理器环境下软件的正确运行至关重要。为了更好地理解内存屏障的作用和它在硬件层面的表现,我们有必要先了解CPU缓存...

    Java多线程之volatile关键字及内存屏障实例解析

    内存屏障的概念内存屏障,也称为内存栅栏,是CPU或编译器用来限制特定操作的指令,它可以保证特定操作的顺序,以及保证某些数据的可见性。在Java中,volatile关键字的实现就依赖于内存屏障。内存屏障分为写屏障和读...

    wx-chevalier#Concurrent-Series#内存屏障1

    写屏障:强制将写缓冲器中的内容写入到高速缓存中,或者将屏障之后的指令全部写到写缓冲器直到之前写缓冲器中的内容全部被刷回缓存中,也就是处理 0 必须等到所有的 i

    深入硬件心脏:汇编语言中的指令锁定与内存屏障解密

    2. **低级语言**:汇编语言直接操作硬件,包括内存地址、寄存器和输入/输出端口。 3. **可读性**:虽然比机器码更易于理解,但仍然需要特定的知识和技能来编写和维护。 4. **效率**:由于直接控制硬件,汇编语言编写...

    memory-barriers.pdf

    内存屏障是计算机科学中的一个概念,主要用于多核处理器或多处理器环境中,确保数据的可见性和一致性。在Linux内核中,内存屏障是用来控制指令执行顺序的机制,以保证不同处理器或不同硬件间的一致性操作。内存屏障...

    内存相关论文

    ### 内存屏障:硬件视角下的软件黑客技术 本文探讨了内存屏障在现代多处理器系统中的重要性及其背后的原理。作者保罗·麦肯尼(Paul E. McKenney)是IBM Beaverton Linux Technology Center的一名专家,他深入剖析...

Global site tag (gtag.js) - Google Analytics