原文地址:http://blog.csdn.net/penngrove/article/details/44175387

最近看到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排他地使用内存。

Linux Kernel CMPXCHG函数分析的更多相关文章

  1. Linux kernel workqueue机制分析

    Linux kernel workqueue机制分析 在内核编程中,workqueue机制是最常用的异步处理方式.本文主要基于linux kernel 3.10.108的workqueue文档分析其基 ...

  2. Linux Kernel‘ieee80211_radiotap_iterator_init()’函数拒绝服务漏洞

    漏洞名称: Linux Kernel‘ieee80211_radiotap_iterator_init()’函数拒绝服务漏洞 CNNVD编号: CNNVD-201312-041 发布时间: 2013- ...

  3. Linux kernel ‘qeth_snmp_command’函数缓冲区溢出漏洞

    漏洞名称: Linux kernel ‘qeth_snmp_command’函数缓冲区溢出漏洞 CNNVD编号: CNNVD-201311-423 发布时间: 2013-11-29 更新时间: 201 ...

  4. Linux kernel ‘aac_send_raw_srb’函数输入验证漏洞

    漏洞名称: Linux kernel ‘aac_send_raw_srb’函数输入验证漏洞 CNNVD编号: CNNVD-201311-422 发布时间: 2013-11-29 更新时间: 2013- ...

  5. Linux kernel ‘lbs_debugfs_write’函数数字错误漏洞

    漏洞名称: Linux kernel ‘lbs_debugfs_write’函数数字错误漏洞 CNNVD编号: CNNVD-201311-421 发布时间: 2013-11-29 更新时间: 2013 ...

  6. Linux kernel ‘xfs_attrlist_by_handle()’函数缓冲区溢出漏洞

    漏洞名称: Linux kernel ‘xfs_attrlist_by_handle()’函数缓冲区溢出漏洞 CNNVD编号: CNNVD-201311-392 发布时间: 2013-11-29 更新 ...

  7. Linux kernel ‘uio_mmap_physical’函数缓冲区溢出漏洞

    漏洞名称: Linux kernel ‘uio_mmap_physical’函数缓冲区溢出漏洞 CNNVD编号: CNNVD-201311-154 发布时间: 2013-11-13 更新时间: 201 ...

  8. Linux Kernel ‘write_tag_3_packet()’函数本地基于堆的缓冲区溢出漏洞

    漏洞名称: Linux Kernel ‘write_tag_3_packet()’函数本地基于堆的缓冲区溢出漏洞 CNNVD编号: CNNVD-201311-067 发布时间: 2013-11-07 ...

  9. Linux Kernel ‘exitcode_proc_write()’函数本地缓冲区溢出漏洞

    漏洞名称: Linux Kernel ‘exitcode_proc_write()’函数本地缓冲区溢出漏洞 CNNVD编号: CNNVD-201311-061 发布时间: 2013-11-07 更新时 ...

随机推荐

  1. IOS CoreData 多表查询demo解析

    在IOS CoreData中,多表查询上相对来说,没有SQL直观,但CoreData的功能还是可以完成相关操作的. 下面使用CoreData进行关系数据库的表与表之间的关系演示.生成CoreData和 ...

  2. 将apk安装包安装在Android真机或者模拟器

    例子如下: 一.准备 打开MAC PC上的Android模拟器方法:打开eclipse-—>window->Android Virtual Device Manager 如果是安装在真机上 ...

  3. IP分片浅析

    在TCP/IP分层中,数据链路层用MTU(Maximum Transmission Unit,最大传输单元)来限制所能传输的数据包大小,MTU是指一次传送的数据最大长度,不包括数据链路层数据帧的帧头, ...

  4. C编译错误解决方法

    1.expected identifier before numeric constant 一般情况下是枚举类型中的某个变量已经被#define定义过一次了,在项目空间中搜索你枚举类型中的所有变量类型 ...

  5. hadoop-ha QJM架构应用故障总结

    部署hadoop-ha QJM架构过程我就不说了,参考 我的博客:hadoop-ha QJM架构部署故障一:    namenode 报错日志如下: WARN org.apache.hadoop.hd ...

  6. DevSecOps 简介(一)

    DevOps,或者说企业应用开发团队和系统运营团队的合作,已经成为一个时髦的 IT 话题.这一新的运营模式往往与敏捷式软件开发方法并举,同时还会利用云计算的可扩展性--这一切,都是为了使企业更加灵活, ...

  7. 【面试题041】和为s的两个数字VS和为s的连续正数序列

    [面试题041]和为s的两个数字VS和为s的连续正数序列 题目一:     输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s.如果有多对数字的和等于s,输出任意一对即可. ...

  8. .htaccess的基本作用及相关语法介绍

    .htaccess是一个纯文本文件,它里面存放着Apache服务器配置相关的指令. .htaccess主要的作用有:URL重写.自定义错误页面.MIME类型配置以及访问权限控制等.主要体现在伪静态的应 ...

  9. Visual Studio 项目中添加include, lib, dll库文件(*.h,*.lib,*.dll)

    应用程序使用外部库时需要进行加载,两种库的加载本质上都是一样:提供功能和功能的定义.vs2005 c++ 项目设置外部库方法如下: 1. 添加编译所需要(依赖)的 lib 文件     在“项目-&g ...

  10. linux c截断文件

    http://www.cnblogs.com/zhuxiongfeng/archive/2010/08/24/1807505.html