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排他地使用内存。
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 【投稿】刀哥:Rust学习笔记 4
- Spring Boot 运行源码剖析
- Spring Boot 特性之 Lazy
- Spring Boot 特性之 Banner
- Spring Boot 特性之 Fluent Builder API
- 一文搞懂 Flink Timer
- DATE类型的“小陷阱”
- 简单聊下 Java Agent
- Byte Buddy 基础知识
- 两种在SAP Cloud Application Studio里通过编程对C4C UI字段赋值的方法
- 如何使用 BTrace v.2.0.1
- 三分钟写一个 Java 多线程
- 使用纯粹的ABAP位操作实现两个整数相加
- 如何给SAP Cloud for Customer UI上的字段添加自定义校验逻辑
- guide-rpc-framework 源码学习