原文地址: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. NYOJ-655 光棍的YY AC 分类: NYOJ 2013-12-29 19:24 224人阅读 评论(0) 收藏

    #include<stdio.h> #include<string.h> char str[210]; int max[210][52]={0}; int sum(int n, ...

  2. The 15th Zhejiang University Programming Contest

    a  ZOJ 3860 求和大家不一样的那个数,签到,map水之 #include<cstdio> #include<map> using namespace std; map ...

  3. .NET中JSON的序列化和反序列化

    .NET 中有两种方法进行JSON的操作分别需要引用不同的命名空间 1.System.Runtime.Serialization.Json(System.Runtime.Serialization.d ...

  4. FastDfs点滴

    1.centos安装后提示找不到libevent动态库 根据系统是64位版本还是32位版本,若是64位版本则默认回到 /usr/lib64 目录下查找,而对于32位则到 /usr/lib 目录下查找. ...

  5. 程序员必须知道的git托管平台

    http://www.open-open.com/lib/view/open1420704561390.html

  6. POJ 2013

    #include <iostream> #include <string> #define MAXN 20 using namespace std; string _m[MAX ...

  7. URAL 1167. Bicolored Horses (DP)

    题目链接 题意 :农夫每天都会放马出去,然后晚上把马赶入马厩,于是让马排成一行入马厩,但是不想马走更多的路,所以让前p1匹入第一个马厩,p2匹马入第二个马厩…………但是他不想让他的任何一个马厩空着,所 ...

  8. copy

    拷贝文件,不覆盖重复文件 yes no|cp -i a b

  9. linux中的磁盘的MBR记录详解

    在硬盘中,硬盘的0柱面0磁头第一个1扇区称为主引导扇区,也叫主引导记录-MBR(main boot record),其中MBR是以下三个部分组成 1.Bootloader,主引导程序---446个字节 ...

  10. 传入sql数组字符串,输出table

    CREATE function [dbo].[split](@aString varchar(),@pattern varchar()) returns @temp table([Sid] [, ) ...