lock 指令前缀与mutex锁

lock指令前缀

原子操作是不可分割的操作,在执行完毕时它不会被任何事件中断。在单处理器中,能够在单条指令中完成的操作都可以认为是原子操作,因为中断只发生在指令与指令之间。

在多处理器系统中情况有所不同,由于系统中有多个处理器在独立运行,即使在能单条指令中完成的操作也可能受到干扰。

在所有的X86的CPU上都具有锁定一个特定内存地址的能力,当这个特定内存地址被锁定之后,它就可以阻止其他的系统总线读取或修改这个内存地址。这种能力是通过Lock指令前缀加上下面的汇编指令来实现的。当使用Lock前缀时,它会使CPU宣告一个Lock#信号,这样就能确保在多处理器系统或多线程竞争的环境下互斥地使用这个内存地址。当指令执行完毕,这个锁定动作也就会消失。

能够和Lock指令前缀一起使用的指令如下:

BT, BTS, BTR, BTC   (mem, reg/imm)
XCHG, XADD  (reg, mem / mem, reg)
ADD, OR, ADC, SBB   (mem, reg/imm)
AND, SUB, XOR   (mem, reg/imm)
NOT, NEG, INC, DEC  (mem)

注意:XCHG 和 XADD (以及所有以 'X' 开头的指令)都能够保证在多处理器系统下的原子操作,它们总会宣告一个 "LOCK#" 信号,而不管有没有 LOCK 前缀。

CAS就是是通过XCHG指令实现的。

在一些处理器,包括P6家族,奔腾4(Pentium4)系列,至强(Xeon)处理器,lock操作可能不会宣告一个Lock#信号。从P6家族处理器开始,当使用Lock指令访问的内存已经被处理器加载到缓存中时,Lock#信号通常不会被宣告。取而代之的是,锁定了处理器的缓存(即锁缓存,然后通过缓存一致性协议来保障数据一致性)。这里,处理器的缓存一致性机制确保了可以原子性的对内存进行操作。

参考博客中提到,LINUX对于x86而言,在为UP体系统架构下,调用barrier()进行通用内存屏障。在SMP体系架构下,若为64位CPU或支持mfence、lfence、sfence指令的32位CPU,则smp_mb()、smp_rmb()、smp_smb()对应通用内存屏障、写屏障和读屏障;而不支持mfence、lfence、sfence指令的32位CPU则smp_mb()、smp_rmb()、smp_smb()对应LOCK操作。

缓存一致性协议

所有内存传输都发生在一条共享总线上,而所有的处理器都能看到这条总线:缓存本身是独立的,但是内存是共享资源,所有的内存访问都要经过仲裁(同一个指令周期中,只有一个CPU缓存可以读内存)。

CPU缓存不仅仅在做内存传输时才与总线打交道,而是不停在嗅探总线上发生的数据交换,跟踪其他缓存在做什么。所以当一个缓存代表它所属的处理器去读写内存时,其他处理器都会得到通知,它们以此来使自己的缓存保持同步。只要某个处理器一写内存,其它处理器马上知道这块内存在它们的缓存段中已失效。

MESI协议是当前最主流的缓存一致性协议,在MESI协议中,每个缓存行有4个状态,可用2个bit表示,它们分别是:

这里的I、S和M状态已经有了对应的概念:失效/未载入、干净以及脏的缓存段。所以这里新的知识点只有E状态,代表独占式访问,这个状态解决了"在我们开始修改某块内存之前,我们需要告诉其它处理器"这一问题:只有当缓存行处于E或者M状态时,处理器才能去写它,也就是说只有在这两种状态下,处理器是独占这个缓存行的。当处理器想写某个缓存行时,如果它没有独占权,它必须先发送一条"我要独占权"的请求给总线,这会通知其它处理器把它们拥有的同一缓存段的拷贝失效(如果有)。只有在获得独占权后,处理器才能开始修改数据----并且此时这个处理器知道,这个缓存行只有一份拷贝,在我自己的缓存里,所以不会有任何冲突。

反之,如果有其它处理器想读取这个缓存行(马上能知道,因为一直在嗅探总线),独占或已修改的缓存行必须先回到"共享"状态。如果是已修改的缓存行,那么还要先把内容回写到内存中。

Futex

Futex 是fast userspace mutex的缩写,意思是快速用户空间互斥体。Linux内核把它们作为快速的用户空间的锁和信号量的预制构件提供给开发者。Futex非常基础,借助其自身的优异性能,构建更高级别的锁的抽象,如POSIX互斥体。大多数程序员并不需要直接使用Futex,它一般用来实现像NPTL这样的系统库。

Futex 由一块能够被多个进程共享的内存空间(一个对齐后的整型变量)组成;这个整型变量的值能够通过汇编语言调用CPU提供的原子操作指令来增加或减少,并且一个进程可以等待直到那个值变成正数。Futex 的操作几乎全部在应用程序空间完成;只有当操作结果不一致从而需要仲裁时,才需要进入操作系统内核空间执行。这种机制允许使用 futex 的锁定原语有非常高的执行效率:由于绝大多数的操作并不需要在多个进程之间进行仲裁,所以绝大多数操作都可以在应用程序空间执行,而不需要使用(相对高代价的)内核系统调用。

futex保存在用户空间的共享内存中,并且通过原子操作进行操作。在大部分情况下,资源不存在争用的情况下,进程或者线程可以立刻获得资源成功,实际上就没有必要调用系统调用,陷入内核了。实际上,futex的作用就在于减少系统调用的次数,来提高系统的性能。

lock指令前缀和mutex锁的区别

mutex是操作系统实现互斥锁的一种方式,而lock指令前缀是保证mutex互斥锁实现原子性操作的更底层的机制

synchronized的重量级锁与mutex锁

重量级锁通过对象内部的监视器(monitor)实现,其中monitor的本质是依赖于底层操作系统的Mutex Lock实现,这需要从用户态切换到内核态,切换成本非常高。

 

参考博客:

1、https://blog.csdn.net/zacklin/article/details/7445442 原子操作与 x86 上的 lock 指令前缀

2、https://www.cnblogs.com/xrq730/p/7048693.html 就是要你懂Java中volatile关键字实现原理

3、https://blog.csdn.net/u011244446/article/details/52574369 Linux 互斥锁、原子操作实现原理

4、https://www.jianshu.com/p/6745203ae1fe 关于volatile、MESI、内存屏障、#Lock

5、https://www.jianshu.com/p/61490effab35 操作系统中锁的原理

6、https://www.cnblogs.com/XiaoHDeBlog/p/3740270.html Linux中同步互斥机制研究之原子操作

7、https://blog.csdn.net/Saintyyu/article/details/100838503 cas vs mutex

8、https://blog.csdn.net/qq_36299025/article/details/89883989 CAS原子操作

9、https://www.cnblogs.com/icanth/archive/2012/06/10/2544300.html   LINUX内核之内存屏障

 

 

 

  • 5
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值