Linux Kernel CMPXCHG函数分析

最近看到Linux Kernel cmpxchg的代码,对实现很不理解。上网查了内嵌汇编以及Intel开发文档,才慢慢理解了,记录下来以享和我一样困惑的开发者。其实cmpxchg实现的原子操作原理早已被熟知:

cmpxchg(void* ptr, int old, int new),如果ptr和old的值一样,则把new写到ptr内存,否则返回ptr的值,整个操作是原子的。在Intel平台下,会用lock cmpxchg来实现,这里的lock个人理解是锁住内存总线,这样如果有另一个线程想访问ptr的内存,就会被block住。

好了,让我们来看Linux Kernel中的cmpxchg(网上找来的,我自己机器上没找到对应的头文件,据说在include/asm-i386/cmpxchg.h)实现:

01./* TODO: You should use modern GCC atomic instruction builtins instead of this. */  
02.#include <stdint.h>  
03.#define cmpxchg( ptr, _old, _new ) { \  
04.  volatile uint32_t *__ptr = (volatile uint32_t *)(ptr);   \  
05.  uint32_t __ret;                                     \  
06.  asm volatile( "lock; cmpxchgl %2,%1"           \  
07.    : "=a" (__ret), "+m" (*__ptr)                \  
08.    : "r" (_new), "0" (_old)                     \  
09.    : "memory");                 \  
10.  );                                             \  
11.  __ret;                                         \  
12.}  
/* TODO: You should use modern GCC atomic instruction builtins instead of this. */
#include <stdint.h>
#define cmpxchg( ptr, _old, _new ) { \
  volatile uint32_t *__ptr = (volatile uint32_t *)(ptr);   \
  uint32_t __ret;                                     \
  asm volatile( "lock; cmpxchgl %2,%1"           \
    : "=a" (__ret), "+m" (*__ptr)                \
    : "r" (_new), "0" (_old)                     \
    : "memory");				 \
  );                                             \
  __ret;                                         \
}

主要要看懂内嵌汇编,c的内嵌汇编格式是

01.asm ( assembler template  
02.    : output operands                   (optional)  
03.    : input operands                    (optional)  
04.    : clobbered registers list          (optional)  
05.    );  
asm ( assembler template
    : output operands                   (optional)
    : input operands                    (optional)
    : clobbered registers list          (optional)
    );

output operands和inpupt operands指定参数,它们从左到右依次排列,用','分割,编号从0开始。以cmpxchg汇编为例,(__ret)对应0,(*__ptr)对应1,(_new)对应2,(_old)对应3,如果在汇编中用到"%2",那么就是指代_new,"%1"指代(*__ptr)。

"=a"是说要把结果写到__ret中,而且要使用eax寄存器,所以最后写结果的时候是的操作是mov eax, ret (eax==>__ret)。"r" (_new)是要把_new的值读到一个通用寄存器中使用。

在cmpxchg中,注意"0"(_old),这个是困惑我的地方,它像告诉你(_old)和第0号操作数使用相同的寄存器或者内存,即(_old)的存储在和0号操作数一样的地方。在cmpxchg中,就是说_old和__ret使用一样的寄存器,而__ret使用的寄存器是eax,所以_old也用eax。

明白了这些,再来看cmpxchgl,在Intel开发文档上说:

0F B1/r        CMPXCHG r/m32, r32           MR Valid Valid*          Compare EAX with r/m32. If equal, ZF is set                                                                                                      and r32 is loaded into r/m32. Else, clear ZF                                                                                                      and load r/m32 into EAX.

翻译一下:

比较eax和目的操作数(第一个操作数)的值,如果相同,ZF标志被设置,同时源操作数(第二个操作)的值被写到目的操作数,否则,清ZF标志,并且把目的操作数的值写回eax。

好了,把上面这句话套在cmpxchg上就是:

比较_old和(*__ptr)的值,如果相同,ZF标志被设置,同时_new的值被写到(*__ptr),否则,清ZF标志,并且把(*__ptr)的值写回_old。

很明显,符合我们对cmpxchg的理解。

另:Intel开发手册上说lock就是让CPU排他地使用内存。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏老马寒门IT

Node入门教程(8)第六章:path 模块详解

path 模块详解 path 模块提供了一些工具函数,用于处理文件与目录的路径。由于windows和其他系统之间路径不统一,path模块还专门做了相关处理,屏蔽...

31280
来自专栏土豆专栏

Java面试之基本概念(一)

首先heap是堆,stack是栈,stack的空间是由操作系统自动分配和释放,heap的空间是手动申请和释放的;heap的空间是手动申请和释放的,heap常用n...

27070
来自专栏开发技术

spring-boot-2.0.3不一样系列之源码篇 - SpringApplication的run方法(一)之SpringApplicationRunListener,绝对有值得你看的地方

  Springboot启动源码系列还只写了一篇,已经过去一周,又到了每周一更的时间了(是不是很熟悉?),大家有没有很期待了?我会尽量保证启动源码系列每周一更,...

21220
来自专栏Jed的技术阶梯

Apache Avro 入门

Apache Avro(以下简称 Avro)是一种与编程语言无关的序列化格式。Doug Cutting 创建了这个项目,目的是提供一种共享数据文件的方式。

67110
来自专栏JAVA技术zhai

并发编程的艺术

关于 Java 并发也算是写了好几篇文章了,本文将介绍一些比较基础的内容,注意,阅读本文需要一定的并发基础。

14720
来自专栏技术墨客

Hazelcast集群服务(4)——分布式Map

    在第一篇介绍Hazelcast的文章已经提到,Hazelcast为Java中绝大部分数据结构提供了分布式实现。我们常用的Map、List、Queue等数...

41330
来自专栏陈树义

JVM系列第4讲:从源代码到机器码,发生了什么?

在上篇文章我们聊到,无论什么语言写的代码,其到最后都是通过机器码运行的,无一例外。那么对于 Java 语言来说,其从源代码到机器码,这中间到底发生了什么呢?这就...

17720
来自专栏Java 源码分析

synchronized 原理分析

synchronized 原理分析 1. 在阅读源码时做了大量的注释,并且做了一些测试分析源码内的执行流程,由于博客篇幅有限,并且代码阅读起来没有 IDE 方...

28230
来自专栏编程

代码生成利器:IDEA 强大的 Live Templates

文章转自:http://blog.xiaohansong.com/2017/03/17/idea-live-templates/ 前言 Java 开发过程经常需...

51750
来自专栏Android群英传

Retrofit源码分析

15140

扫码关注云+社区

领取腾讯云代金券