转自:http://www.linuxidc.com/Linux/2011-10/44623.htm

前言
之前读了关于顺序一致性和缓存一致性讨论的文章,感觉豁然开朗。对linux内核中出现的种种同步和屏障,想做一点总结。

缓存一致性
之前一直认为linux中很多东西是用来保证缓存一致性的,其实不是。缓存一致性绝大部分是靠硬件机制实现的,只有在带lock前缀的指令执行时才与cache有一点关系。(这话说得绝对,但我目前看来就是这样)我们更多的时候是为了保证顺序一致性。
-

所谓缓存一致性,就是在多处理器系统中,每个cpu都有自己的L1 cache。很可能两个不同cpu的L1 cache中缓存的是同一片内存的内容,如果一个cpu更改了自己被缓存的内容,它要保证另一个cpu读这块数据时也要读到这个最新的。不过你不要担心,这个复杂的工作完全是由硬件来完成的,通过实现一种MESI协议,硬件可以轻松的完成缓存一致性的工作。不要说一个读一个写,就是多个同时写都没问题。一个cpu读时总能读入最新的数据,不管它是在自己的cache中,还是在其它cpu的cache中,还是在内存中,这就是缓存一致性。

顺序一致性
所谓顺序一致性,说的则是与缓存一致性完全不同的概念,虽然它们都是处理器发展的产物。因为编译器的技术不断发展,它可能为了优化你的代码,而将某些操作的顺序更改执行。处理器中也早就有了多发射、乱序执行的概念。这样的结果,就是实际执行的指令顺序会与编程时代码的执行顺序略有不同。这在单处理器下当然没什么,毕竟只要自己的代码不过问,就没人过问,编译器和处理器就是在保证自己的代码发现不了的情况下打乱执行顺序的。但多处理器不是这样,可能一个处理器上指令的完成顺序,会对其它处理器上执行的代码造成很大影响。所以就有了顺序一致性的概念,即保证一个处理器上线程的执行顺序,在其它的处理器上的线程看来,都是一样的。这个问题的解决不是光靠处理器或者编译器就能解决的,需要软件的干预。

内存屏障
软件干预的方式也非常简单,那就是插入内存屏障(memory barrier)。其实内存屏障这个词,是由搞处理器的人造的,弄得我们很不好理解。内存屏障,很容易让我们串到缓存一致性去,乃至怀疑是否这样做才能让其它cpu看到被修改过的cache,这样想就错了。所谓内存屏障,从处理器角度来说,是用来串行化读写操作的,从软件角度来讲,就是用来解决顺序一致性问题的。编译器不是要打乱代码执行顺序吗,处理器不是要乱序执行吗,你插入一个内存屏障,就相当于告诉编译器,屏障前后的指令顺序不能颠倒,告诉处理器,只有等屏障前的指令执行完了,屏障后的指令才能开始执行。当然,内存屏障能阻挡编译器乱来,但处理器还是有办法。处理器中不是有多发射、乱序执行、顺序完成的概念吗,它在内存屏障时只要保证前面指令的读写操作,一定在后面指令的读写操作完成之前完成,就可以了。所以内存屏障才会对应有读屏障、写屏障和读写屏障三类。如x86之前保证写操作都是顺序完成的,所以不需要写屏障,但现在也有部分ia32处理器的写操作变成乱序完成,所以也需要写屏障。
    其实,除了专门的读写屏障指令,还有很多指令的执行是带有读写屏障功能的,比如带lock前缀的指令。在专门的读写屏障指令出现之前,linux就是靠lock熬过来的。
    至于在那里插入读写屏障,要视软件的需求而定。读写屏障无法完全实现顺序一致性,但多处理器上的线程也不会一直盯着你的执行顺序看,只要保证在它看过来的时候,认为你符合顺序一致性,执行不会出现你代码中没有预料到的情况。所谓预料外的情况,举例而言,你的线程是先给变量a赋值,再给变量b赋值,结果别的处理器上运行的线程看过来,发现b赋值了,a却没有赋值,(注意这种不一致不是由缓存不一致造成的,而是处理器写操作完成的顺序不一致造成的),这时就要在a赋值与b赋值之间,加一个写屏障。

多处理器间同步
      有了SMP之后,线程就开始同时在多个处理器上运行。只要是线程就有通信和同步的要求。幸好SMP系统是共享内存的,也就是所有处理器看到的内存内容都一样,虽然有独立的L1 cache,但还是由硬件完成了缓存一致性处理的问题。那不同处理器上的线程要访问同一数据,需要临界区,需要同步。靠什么同步?之前在UP系统中,我们上靠信号量,下靠关中断和读修改写指令。现在在SMP系统中,关中断已经废了,虽然为了同步同一处理器上的线程还是需要的,但只靠它已经不行了。读修改写指令?也不行了。在你指令中读操作完成写操作还没进行时,就可能有另外的处理器进行了读操作或者写操作。缓存一致性协议是先进,但还没有先进到预测这条读操作是哪种指令发出来的。所以x86又发明了带lock前缀的指令。在此指令执行时,会将所有包含指令中读写地址的cache line失效,并锁定内存总线。这样别的处理器要想对同样的地址或者同一个cache line上的地址读写,既无法从cache中进行(cache中相关line已经失效了),也无法从内存总线上进行(整个内存总线都锁了),终于达到了原子性执行的目的。当然,从P6处理器开始,如果带lock前缀指令 要访问的地址本来就在cache中,就无需锁内存总线,也能完成原子性操作了(虽然我怀疑这是因为加了多处理器内部公共的L2 cache的缘故)。

因为会锁内存总线,所以带lock前缀指令执行前,也会先将未完成的读写操作完成,也起到内存屏障的功能。
     现在多处理器间线程的同步,上用自旋锁,下用这种带了lock前缀的读修改写指令。当然,实际的同步还有加上禁止本处理器任务调度的,有加上任务关中断的,还会在外面加上信号量的外衣。linux中对这种自旋锁的实现,已历经四代发展,变得愈发高效强大。

内存屏障的实现
#ifdef CONFIG_SMP   
#define smp_mb()    mb()   
#define smp_rmb()   rmb()   
#define smp_wmb()   wmb()   
#else   
#define smp_mb()    barrier()   
#define smp_rmb()   barrier()   
#define smp_wmb()   barrier()   
#endif

CONFIG_SMP就是用来支持多处理器的。如果是UP(uniprocessor)系统,就会翻译成barrier()。

#define barrier() __asm__ __volatile__("": : :"memory") 
barrier()的作用,就是告诉编译器,内存的变量值都改变了,之前存在寄存器里的变量副本无效,要访问变量还需再访问内存。这样做足以满足UP中所有的内存屏障。
#ifdef CONFIG_X86_32   
/* 
 * Some non-Intel clones support out of order store. wmb() ceases to be a 
 * nop for these. 
 */  
#define mb() alternative("lock; addl $0,0(%%esp)", "mfence", X86_FEATURE_XMM2)   
#define rmb() alternative("lock; addl $0,0(%%esp)", "lfence", X86_FEATURE_XMM2)   
#define wmb() alternative("lock; addl $0,0(%%esp)", "sfence", X86_FEATURE_XMM)   
#else   
#define mb()    asm volatile("mfence":::"memory")   
#define rmb()   asm volatile("lfence":::"memory")   
#define wmb()   asm volatile("sfence" ::: "memory")   
#endif 
如果是SMP系统,内存屏障就会翻译成对应的mb()、rmb()和wmb()。这里CONFIG_X86_32的意思是说这是一个32位x86系统,否则就是64位的x86系统。现在的linux内核将32位x86和64位x86融合在同一个x86目录,所以需要增加这个配置选项。

可以看到,如果是64位x86,肯定有mfence、lfence和sfence三条指令,而32位的x86系统则不一定,所以需要进一步查看cpu是否支持这三条新的指令,不行则用加锁的方式来增加内存屏障。

SFENCE,LFENCE,MFENCE指令提供了高效的方式来保证读写内存的排序,这种操作发生在产生弱排序数据的程序和读取这个数据的程序之间。
   SFENCE——串行化发生在SFENCE指令之前的写操作但是不影响读操作。
   LFENCE——串行化发生在SFENCE指令之前的读操作但是不影响写操作。
   MFENCE——串行化发生在MFENCE指令之前的读写操作。
sfence:在sfence指令前的写操作当必须在sfence指令后的写操作前完成。
lfence:在lfence指令前的读操作当必须在lfence指令后的读操作前完成。
mfence:在mfence指令前的读写操作当必须在mfence指令后的读写操作前完成。

至于带lock的内存操作,会在锁内存总线之前,就把之前的读写操作结束,功能相当于mfence,当然执行效率上要差一些。

说起来,现在写点底层代码真不容易,既要注意SMP问题,又要注意cpu乱序读写问题,还要注意cache问题,还有设备DMA问题,等等。

多处理器间同步的实现
      多处理器间同步所使用的自旋锁实现,已经有专门的文章介绍,见《spin lock在kernel 2.4与2.6中的实现与改进》。

本篇文章来源于 Linux公社网站(www.linuxidc.com)  原文链接:http://www.linuxidc.com/Linux/2011-10/44623.htm

Linux内核中的内存屏障(转)相关推荐

  1. Linux内核中常见内存分配函数

    1.      原理说明 Linux内核中采用了一种同时适用于32位和64位系统的内存分页模型,对于32位系统来说,两级页表足够用了,而在x86_64系统中,用到了四级页表,如图2-1所示.四级页表分 ...

  2. KSM(Kernel Samepage Merging) 剖析:Linux 内核中的内存去耦合

    简介:作为一个系统管理程序(hypervisor),Linux® 有几个创新,2.6.32 内核中一个有趣的变化是 KSM(Kernel Samepage Merging)  允许这个系统管理程序通过 ...

  3. Linux内核中的内存管理(图例解析)

    一 ,内核管理内存的方式 (1)内核把物理页作为内存管理的基本单位,内存管理单元通常以页为单位进行处理,所以,从虚拟内存角度来看,页就是最小单位. 大多数32位系统支持4kb的页,64位系统支持8kb ...

  4. Linux内核中使用内存检测

    目录 一.slub内存检测 1.越界访问 2.释放后再访问 3.无效的释放 4.实验输出 二.KASAN 内存检测 1.数组越界 2.栈的越界访问 3.实验输出 一般的内存访问错误如下: 越界访问 访 ...

  5. [十月往昔]——Linux内核中的内存管理浅谈

    为什么要叫做"十月往昔"呢,是为了纪念我的原博客,http://www.casual0402.cn. 不知道为什么,突然想来一个新的开始--而那个博客存活至今刚好十个月,也有十个月 ...

  6. Linux内核中内存分配函数

    1.原理说明 Linux内核 中采 用了一种同时适用于32位和64位系统的内 存分页模型,对于32位系统来说,两级页表足够用了,而在x86_64系 统中,用到了四级页表,如图2-1所示.四级页表分别为 ...

  7. 红黑树在linux内核中的应用场景(红黑树,进程管理CFS,内存管理)丨epoll丨c/c++linux服务器开发丨linux后台开发

    红黑树在linux内核中的应用场景(红黑树,进程管理CFS,内存管理) 视频讲解如下: 红黑树在linux内核中的应用场景(红黑树,进程管理CFS,内存管理)丨epoll丨c/c++linux服务器开 ...

  8. Linux内核中内存管理相关配置项的详细解析3

    接前一篇文章:Linux内核中内存管理相关配置项的详细解析2 5. 2:1 compression allocator (zbud) 对应配置变量为:CONFIG_ZBUD. 此项默认为选中(如果前一 ...

  9. linux 信号量锁 内核,Linux内核中锁机制之信号量、读写信号量

    在上一篇博文中笔者分析了关于内存屏障.读写自旋锁以及顺序锁的相关内容,本篇博文将着重讨论有关信号量.读写信号量的内容. 六.信号量 关于信号量的内容,实际上它是与自旋锁类似的概念,只有得到信号量的进程 ...

最新文章

  1. Django 数据库
  2. D001斯图加特~计算机
  3. C++ :: 的用法小结
  4. Mockito对final类型和方法的支持(三):免配置的inline mock making
  5. jQuery UI - Accordion 手风琴组件的使用
  6. mybatis 控制台打印sql脚本
  7. 猎头职位:存储翘首EMC高薪诚聘高级软件工程师SH
  8. aws mysql链接_AWS Lambda和MySQL连接处理
  9. vue axios封装以及登录token过期跳转问题
  10. JavaScript将JSON转换为字符串
  11. python工具包:Python Swiftclient
  12. Excel如何计算两列数据的乘积之和(相乘之后相加)
  13. 客房管理系统前台代码html,客房管理系统|客房软件|PMS系统|酒店管理系统|酒店管理软件...
  14. 基于javaweb的教室信息管理系统
  15. cad工具箱详细讲解_CAD的工具说明及使用讲解
  16. 美的微晶冰箱以云数据为驱动,实现智能保鲜冰箱智慧新升级
  17. 中国云计算厂商TOP30重磅榜单
  18. 华为机器学习服务语音识别功能,让应用绘“声”绘色
  19. 乌班图18.04搭建ssh服务器
  20. 模拟button按钮按下

热门文章

  1. 纽约大街上的免费WiFi,终于铺起来了
  2. Gartner认为安全性将取代成本和敏捷性成为政府部门采用云服务的首要原因
  3. TODO:从数据库中随机抽取一条记录
  4. [转]张孟苏考上的不是大学
  5. shell脚本--cut命令
  6. IE8 兼容background-size的解决办法
  7. 研究生计划-心得征程
  8. CLS/CLR、c#(等语言)、托管、元数据、程序集 CLR学习第二课
  9. 桌面程序explorer_备份Internet Explorer 7搜索提供程序列表
  10. 在Windows 7中的Windows Media Player 12中快速预览歌曲