Linux内核中的内存屏障(转)

转自: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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/302213.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微软Build2021今日召开,共同期待VS2022+.NET6!

Microsoft Build 2021全球开发者大会将至,将带来什么惊喜呢?去年Build 2020是第一次完全线上举办的Build大会,是第一次完全属于开发者的大会,几乎所有的新产品都是属于开发者,开发者是唯一的主角!今年的Bui…

mysql group 条件,mysql - mysql group by,两个条件,限制1 - SO中文参考 - www.soinside.com...

我想为所有门票选择最早的活动日期。我在mysql中创建了这个查询“tixdate”视图(我用于其他目的),它按票号排序记录,然后按eventdate排序。此视图如下所示:EventDate | Ticket2018-02-25 | 9982018-02-25 | 9982018-02-25 | 9982018-02-11 | …

Google和百度都无法替代的10大深网搜索引擎

全世界只有3.14 % 的人关注了数据与算法之美当我们想要搜索某些内容时,我们第一个想到的就是打开Google、百度或必应这类的搜索引擎。但针对有些内容,却是这些常规搜索引擎无法获取到的,那就是隐藏在深网的内容。据不完全统计,深网…

编写properties文件的Eclipse插件

2019独角兽企业重金招聘Python工程师标准>>> 分享一个不错的编写properties文件的Eclipse插件(plugin),有了它我们在编辑一些简体中文、繁体中文等 Unicode文本时,就不必再使用native2ascii编码了。您可以通过Eclipse中…

让Dapper支持读写分离

在上一篇说了封装Dapper扩展方法为一个接口来支持Mock,接下来看看如何实现读写分离。其实定义两个接口,一个用来实现读,一个用来实现写。在读的接口里只有Query的方法,在写的接口里实现Query和Execute全量(通读写的库也是支持读的…

php显示前60个字,DEDECMS中怎么让文章标题栏突破60个字符

DEDECMS中怎么让文章标题栏突破60个字符?1、使用PHPMYADMIN 修改 MYSQL数据结构CODE: ALTER TABLE dede_archives CHANGE title title VARCHAR( 250 ) [Copy to clipboard]2、打开/dede/action_article_save.php找到39行 CODE: $title cn_substr($title,60); [Copy…

数学是理工基础,如何才能令人信服?

随着科技的快速发展,人工智能的重要性日渐显现。而数学知识蕴含着处理智能问题的基本思想与方法,是理解复杂算法的必备要素。在机器学习工作流程中,数学与代码高度交织在一起,代码通常可以根据数学直观地构建,甚至会共…

GNU make manual 翻译(四十三)

继续翻译 Another such occasion is when you want to generate prerequisites from source files automatically; the prerequisites can be put in a file that is included by the main makefile. This practice is generally cleaner than that of somehow appending …

巧用定时任务监控第三方组件是否正常

背景平常我们系统一般会涉及到一些使用第三方组件的情况,那么我们如何去监测,第一时间知道组件是否可用。或者是组件没报错,但是没法返回我们想要的数据。问题窥探一般做法是在调用的时候,如果是异常,就发出对应的报警…

php调用另一个php文件里的变量的值,thinkphp中一个方法调用另一个步骤的变量

thinkphp中一个方法调用另一个方法的变量//实例化上传类public function upload() {$upload new Upload();$upload->maxSize 10 * 1024 * 1024;$upload->exts array(jpg, jpeg, gif, txt,zip);$upload->savePath ./;$upload->hash false;$info $upload->u…

Win7玩CF,不能全屏的解决方法...

今天用自己的本本玩CF,发天竟然不能全屏,抓狂呀! 在网上找了下,解决方法如下: 打开注册表,定位到: HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\GraphicsDrivers\Configuration\AUO183C0_01_07D9_17^773484D7596…

EFCore之增删改查

1. 连接数据库通过依赖注入配置应用程序&#xff0c;通过startup类的ConfigureService方法中的AddDbContext将EFCore添加到依赖注入容器public void ConfigureServices(IServiceCollection services) {services.AddControllers();services.AddDbContext<OpenDbContext>(o…

matlab提示未定义wc,WooCommerce 教程:修复致命错误调用未定义的函数wc_get_order() - WooCommerce 微站...

我为客户开发了一个自定义支付网关插件&#xff0c;他们希望为 “鳕鱼”(货到付款)添加类似的方法。他希望增加一种称为 “交货卡” 的方法。我只是复制代码&#xff0c;将 PHP 添加到一个文件中&#xff0c;制作了一个插件&#xff0c;并给他插件 zip 文件。一切都很好&#x…

一堂儿童科学实验课引起的思考:数学和化学有什么关系?

全世界只有3.14 % 的人关注了数据与算法之美前段时间&#xff0c;我带侄子上了一堂化学课&#xff0c;回来之后&#xff0c;他一直意犹未尽找我的聊化学的事&#xff0c;期间他也问了身为数学专业的我一个交叉问题&#xff1a;叔&#xff0c;「数学」和「化学」有啥关系&#x…

自学网页设计

目前&#xff0c;我正在努力的学习网页设计&#xff0c;我是通过我要自学网http://www.51zxw.net/study.asp?vip5720219来学习的&#xff0c;还不错哦&#xff0c;加油!有时间大家也可以去看看&#xff0c;里面想学什么都可以找到的&#xff01;转载于:https://blog.51cto.com…

使用 KubernetesClient 操作 kubernetes

使用 KubernetesClient 操作 kubernetesIntro我们的应用都是部署在 Kubernetes 上的&#xff0c;我们有一个服务内部有一层 MemoryCache&#xff0c;之前会依赖 Redis 的 Pub/Sub 来做缓存的更新&#xff0c;而 Redis 的 Pub/Sub 是一种不可靠的更新机制&#xff0c;容易发生消…

cent os重置mysql,linux mysql 能登陆不能修改用户(cent os 6.2)解决思路

linux mysql 能登陆不能修改用户(cent os 6.2)[root3mao /]# select user,host,password from mysql.userbash: syntax error near unexpected token from[root3mao /]# mysql -u rootWelcome to the MySQL monitor. Commands end with ; or /g.Your MySQL connection id is 4S…

本、硕、博到底有什么区别?清华教授的“兔子理论”让你快速弄懂

全世界只有3.14 % 的人关注了数据与算法之美前段时间&#xff0c;有人问到卢sir一个问题——“本、硕、博之间到底有什么区别&#xff1f;”曾经就有一位清华大学教授就讨论过这个问题&#xff0c;让我们来看看这位清华教授是如何看待本、硕、博区别的吧。作者 | 阎学通教授清华…

迁移SVN注意事项及操作方法

最近公司要迁移SVN到新服务器&#xff0c;虽说现在GIT貌似更胜一筹&#xff0c;但是相信用svn的公司还是不在少数&#xff0c;就花了点时间把自己迁移的过程整理了一下。 文档中也许还有不足之处&#xff0c;有问题的话&#xff0c;大家可以告诉我&#xff0c;我会在第一时间修…

重磅!微软发布新一代 Teams 开发工具 —— Teams Toolkit!不止VS Code extension!

今天凌晨&#xff08;北京时间 2021 年 5 月 26 日&#xff09;&#xff0c;在一年一度的 Build 大会上&#xff0c;微软正式发布了新一代的 Teams 开发工具 —— Teams Toolkit。截止到 2021 年 4 月份&#xff0c;Microsoft Teams 的日活用户已经达到了惊人的1.45亿&#xff…