本文转载自聊聊CPU的LOCK指令

导语

在多线程操作中,可能最经常被提起的就是数据的可见性、原子性、有序性。不管是硬件方面、软件方面都在这三方面做了很足的工作,才能保证程序的正常运行。

之前发表过一篇文章聊聊缓存一致性协议 如果感兴趣的话可以去阅读一下,里面谈到了缓存一致性的实现和处理过程,读完之后可以仔细去细想一下缓存一致性协议到底解决了什么问题。个人理解缓存一致性协议解决了CPU层面的可见性和一致性问题,阅读到这里可以在这里停下来,仔细回想一下缓存一致性的原理,它通过监听共享总线上消息,对自己缓存中的数据修改不同的状态,来保证数据的一致性,对自己缓存中的数据失效后,下次读取会从主存中直接读取最新的数据 ,可以保证可见性,同时保证各缓存中的数据是一致的。

软件的并发编程一样,其实除了可见性、有序性,在计算机指令在执行的过程中,CPU通过不停地切换线程执行,给每个线程分配CPU时间片来实现多线程机制,一定也会存在原子性问题,在计算机层面是怎么解决原子性问题的,这就我们今天要聊的LOCK#指令,有时也被我们称为总线锁。

LOCK指令作用

Intel 64 and IA-32 Architectures Software Developer’s Manual 中的章节LOCK—Assert LOCK# Signal Prefix 中给出LOCK指令的详细解释

大至翻译之后的意思如下

在CPU的LOCK信号被声明之后,在此期随同执行的指令会转换成原子指令。在多处理器环境中,LOCK信号确保,在此信号被声明之后,处理器独占使用任何共享内存。

在不大多数IA-32和Inter64位处理器中,锁可能在没有LOCK#信号的时情况下发生。请参阅下面的“IA32体系结构兼容性”部分的详细内容。

LOCK前缀只能预加在以下指令前面,并且只能加在这些形式的指令前面,其中目标操作数是内存操作数:add、adc、and、btc、btr、bts、cmpxchg、cmpxch8b,cmpxchg16b,dec,inc,neg,not,or,sbb,sub,xor,xaddxchg

如果LOCK前缀用上述列表中的指令并且源操作数是内存操作数(也就是指令没有对内存进行写操作),可能会出现未定义的操作码异常(ud)。

如果锁前缀与任何不在上面列表中的指令一起使用,也将生成未定义的操作码异常。

xchg指令不管有没有声明LOCK前缀,总是会声明LOCK信号。

锁定前缀通常与BTS指令一起使用,在共享内存环境中,以对内存地址执行读-修改-写操作。

锁定前缀的完整性不受内存字段对齐的影响。对于任意未对齐的字段,可以观察到内存锁定。

此指令的操作在非64位模式和64位模式下是相同的。

从P6系列处理器开始,当使用 LOCK 指令访问的内存已经被处理器加载到缓存中时,LOCK# 信号通常不会断言。取而代之的是,只锁定处理器的缓存。在这里处理器的缓存一致性机制确保对内存进行的操作是原子性的。请参见“锁定操作对内部处理器缓存的影响”,在Intel64和IA-32体系结构软件开发人员手册第3A卷第8章中,有关锁定缓存的详细信息。

大致翻译差不多如上,核心意思主要说明LOCK指令在声明之后通过锁定总线,独占共享内存,通过一种排它的思想确保当前对内存操作的只有一个线程,然后确定在这段声明期间指令执行不会被打断,来保证其原子性。

处理器如何实现原子操作

首先处理器会保证基本的内存操作的原子性,比如从内存读取或者写入一个字节是原子的,但对于读-改-写、或者是其它复杂的内存操作是不能保证其原子性的,又比如跨总线宽度跨多个缓存行夸页表的访问,这时候需要处理器提供总线锁缓存锁两个机制来保证复杂的内存操作原子性

总线锁

LOCK#信号就是我们经常说到的总线锁,处理器使用LOCK#信号达到锁定总线,来解决原子性问题,当一个处理器往总线上输出LOCK#信号时,其它处理器的请求将被阻塞,此时该处理器此时独占共享内存。

总线锁这种做法锁定的范围太大了,导致CPU利用率急剧下降,因为使用LOCK#是把CPU和内存之间的通信锁住了,这使得锁定时期间,其它处理器不能操作其内存地址的数据 ,所以总线锁的开销比较大。

缓存锁

如果访问的内存区域已经缓存在处理器的缓存行中,P6系统和之后系列的处理器则不会声明LOCK#信号,它会对CPU的缓存中的缓存行进行锁定,在锁定期间,其它 CPU 不能同时缓存此数据,在修改之后,通过缓存一致性协议来保证修改的原子性,这个操作被称为“缓存锁”

什么情况下使用总线锁(LOCK#)

当操作的数据不能被缓存在处理器内部,或操作的数据跨多个缓存行时,也会使用总线锁

因为从P6系列处理器开始才有缓存锁,所以对于早些处理器是不支持缓存锁定的,也会使用总线锁

有些指令自带总线锁

BTS、BTR、BTC 、XADD、CMPXCHG、ADD、OR等,这些指令操作的内存区域就会加锁,导致其它处理器不能同时访问它。

在上面指令中的CMPXCHG就是JAVA里面CAS底层常用的指令,这个指令在执行的时候,会自动加总线锁保,导致其它 处理器不能同时访问,证其原子性。

LOCK#作用总结

  1. 锁总线,其它CPU对内存的读写请求都会被阻塞,直到锁释放,因为锁总线的开销比较大,后来的处理器都采用锁缓存替代锁总线,在无法使用缓存锁的时候会降级使用总线锁
  2. lock期间的写操作会回写已修改的数据到主内存,同时通过缓存一致性协议让其它CPU相关缓存行失效

写在最后

总线锁缓存锁可以保证原子性缓存一致性协议可以保证可见性,那么JAVA中的内存模型,它做了些什么?下一篇聊聊JAVA中的内存模型(JMM)聊聊JMM

聊聊CPU的LOCK指令的更多相关文章

  1. 【操作系统之十一】任务队列、CPU Load、指令乱序、指令屏障

    一.CPU Loadcpu load是对使用或者等待cpu进程的统计(数量的累加):每一个使用(running)或者等待(runnable)CPU的进程,都会使load值+1;每一个结束的进程,都会使 ...

  2. 关于缓存一致性协议、MESI、StoreBuffer、InvalidateQueue、内存屏障、Lock指令和JMM的那点事

    前言 事情是这样的,一位读者看了我的一篇文章,不认同我文章里面的观点,于是有了下面的交流. 可能是我发的那个狗头的表情,让这位读者认为我不尊重他.于是,这位读者一气之下把我删掉了,在删好友之前,还叫我 ...

  3. 深入设计电子计算器(一)——CPU框架及指令集设计

    版权申明:本文为博主窗户(Colin Cai)原创,欢迎转帖.如要转贴,必须注明原文网址 http://www.cnblogs.com/Colin-Cai/p/8278418.html 作者:窗户 Q ...

  4. 使用logisim搭建单周期CPU与添加指令

    使用logisim搭建单周期CPU与添加指令 搭建 总设计 借用高老板的图,我们只需要分别做出PC.NPC.IM.RF.EXT.ALU.DM.Controller模块即可,再按图连线,最后进行控制信号 ...

  5. CPU结构与指令执行过程简介

    CPU(Central Processing Unit)是计算机中进行算术和逻辑计算处理指令的主要部件. CPU结构 CPU由通用寄存器组,运算器,控制器和数据通路等部件组成. 寄存器包括 数据寄存器 ...

  6. CPU流水线与指令乱序执行

    青蛙见了蜈蚣,好奇地问:"蜈蚣大哥,我很好奇,你那么多条腿,走路的时候先迈哪一条啊?" 蜈蚣听后说:"青蛙老弟,我一直就这么走路,从没想过先迈哪一条腿,等我想一想再回答你 ...

  7. 使用CPU的AVX指令

    arch:AVX 很抱歉GCC还不行……有……倒是 但是不是这么写的 我忘记了……官网上有 http://www.oschina.net/news/66980/kreogist-0-9

  8. java并发编程知识点备忘

    最近有在回顾这方面的知识,稍微进行一些整理和归纳防止看了就忘记. 会随着进度不断更新内容,比较零散但尽量做的覆盖广一点. 如有错误烦请指正~ java线程状态图 线程活跃性问题 死锁 饥饿 活锁 饥饿 ...

  9. 并发编程之 Java 内存模型 + volatile 关键字 + Happen-Before 规则

    前言 楼主这个标题其实有一种作死的味道,为什么呢,这三个东西其实可以分开为三篇文章来写,但是,楼主认为这三个东西又都是高度相关的,应当在一个知识点中.在一次学习中去理解这些东西.才能更好的理解 Jav ...

随机推荐

  1. HBase,以及GeoMesa设计基于HBase的设计分析,从数据模型到典型查询场景,最后进行RowKey设计

    GeoMesa设计基于HBase的设计分析,从数据模型到典型查询场景,最后进行RowKey设计 一.HBase 基本概念 理解KeyValue KeyValue多版本 列定义(1) 列定义(2) Co ...

  2. Django(图书管理系统)#转

    自己虽然实现了, 但是写的太LOW了,为了不误导大家,推荐一篇好的博客 https://www.cnblogs.com/alice-bj/p/9114084.html

  3. java切割~~百万 十万 万 千 百 十 个 角 分

    /** * @param value * @return */ @SuppressWarnings("unused") public static void convertLoan ...

  4. Java多线程Condition定点通知

    多线程之间按顺序调用,实现A->B->C三个线程启动,要求如下:A打印5次,B打印10次,C打印15次接着 A打印5次,B打印10次,C打印15次 来10轮 package com.yan ...

  5. spark提交命令 spark-submit 的参数 executor-memory、executor-cores、num-executors、spark.default.parallelism分析

    转载:https://blog.csdn.net/zimiao552147572/article/details/96482120 nohup spark-submit --master yarn - ...

  6. 新疆大学ACM新生赛(公开赛) E.异或 (思维,位运算)

    题意:RT 题解: \(i\ mod \ k=0\),即所有事\(k\)的倍数的位置都要进行异或,根据异或的性质,我们知道如果相同的异或的数个数是偶数的话,得出的结果是\(0\),所以每次询问,我们判 ...

  7. C++实现邻接表

    对于无向图(V0,V1),(V1,V2),(V2,V3),(V0,V2)对应的邻接表表示就是 在代码中,你要单独对V1.V2.V3创建一种结构体类型.在对后面的节点0,1,2,3创建一种结构体类型 代 ...

  8. Java 在Word中添加多行图片水印

    Word中设置水印效果时,不论是文本水印或者是图片水印都只能添加单个文字或者图片到Word页面,效果比较单一,本文通过Java代码示例介绍如何在页面中添加多行图片水印效果,即水印效果以多个图片平铺到页 ...

  9. Jpress小程序

    首页轮播.首页公告.首页宫格.个人中心页面均支持在PC后台设置内容 首页列表.分类列表页.搜索列表的文章展示页均支持后台设置,拥有三种风格 所有分类展示支持两种风格 用户中心授权登陆,查看个人数据 J ...

  10. Kafka官方文档V2.7

    1.开始 1.1 简介 什么是事件流? 事件流相当于人体的中枢神经系统的数字化.它是 "永远在线 "世界的技术基础,在这个世界里,业务越来越多地被软件定义和自动化,软件的用户更是软 ...