http://www.longene.org/forum/viewtopic.php?t=2216

前几天,为了这个函数花了好多时间,由于参考的资料有误,一直都没有看明白,直到google之后,总算搞明白了,因此写出来大家分享一下。
在Linux内核中,提供了比较并交换的函数cmpxchg,代码在include/asm-i386/cmpxchg.h中,函数的原型是:
代码: 全选
cmpxchg(void *ptr, unsigned long old, unsigned long new);

函数完成的功能是:将old和ptr指向的内容比较,如果相等,则将new写入到ptr中,返回old,如果不相等,则返回ptr指向的内容。

在linux中的实现是这样的。

代码: 全选
#define cmpxchg(ptr,o,n)\
    ((__typeof__(*(ptr)))__cmpxchg((ptr),(unsigned long)(o),\
                    (unsigned long)(n),sizeof(*(ptr))))

很明显,这个函数就是调用了__cmpxchg。

代码: 全选
static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old,
                      unsigned long new, int size)
{
    unsigned long prev;
    switch (size) {
    case 1:
        __asm__ __volatile__(LOCK_PREFIX "cmpxchgb %b1,%2"
                     : "=a"(prev)
                     : "q"(new), "m"(*__xg(ptr)), "0"(old)
                     : "memory");
        return prev;
    case 2:
        __asm__ __volatile__(LOCK_PREFIX "cmpxchgw %w1,%2"
                     : "=a"(prev)
                     : "r"(new), "m"(*__xg(ptr)), "0"(old)
                     : "memory");
        return prev;
    case 4:
        __asm__ __volatile__(LOCK_PREFIX "cmpxchgl %1,%2"
                     : "=a"(prev)
                     : "r"(new), "m"(*__xg(ptr)), "0"(old)
                     : "memory");
        return prev;
    }
    return old;
}

以最为常用的4字节交换为例,主要的操作就是汇编指令cmpxchgl %1,%2,注意一下其中的%2,也就是后面的"m"(*__xg(ptr))。
__xg是在这个文件中定义的宏:
struct __xchg_dummy { unsigned long a[100]; };
#define __xg(x) ((struct __xchg_dummy *)(x))
那么%2经过预处理,展开就是"m"(*((struct __xchg_dummy *)(ptr))),这种做法,就可以达到在cmpxchg中的%2是一个地址,就是ptr指向的地址。如果%2是"m"(ptr),那么指针本身的值就出现在cmpxchg指令中。

我手头有一份《奔腾指令速查》,其中对cmpxchg的说明是这样的:

代码: 全选
CMPXCHG r/m32,r32 0F B1 /r CMPXCHG EBX,ECX ;如果EAX与EBX相等,则ECX送EBX且ZF置1;否则EBX送ECX,且ZF清0

文章里用的是MS的汇编格式,换成AT&T的格式就是:

代码: 全选
cmpxchg %ecx, %ebx;如果EAX与EBX相等,则ECX送EBX且ZF置1;否则EBX送ECX,且ZF清0

在上述例子中,eax就是old,ebx就是ptr指向的内容,ecx就是new。所以cmpxchg指令的操作就是:如果old等于ptr指向的内容,那么就把new写入到ptr中,返回old(%eax没有改变过,一直是old),这部分和cmpxchg函数的原意是符合的;如果old不等于ptr指向的内容,那么ptr的内容写入new(%ecx)中,返回old(%eax没有改变过,一直是old),这明显不符合cmpxchg函数的意思。对此是大惑不解,后来经过Google才知道,那份资料有错。正解是:

代码: 全选
cmpxchg %ecx, %ebx;如果EAX与EBX相等,则ECX送EBX且ZF置1;否则EBX送EAX,且ZF清0

也就是说,在old和ptr指向的内容不相等的时候,将ptr的内容写入eax中,这样,ptr的内容就会返回给cmpxchg函数的调用者。这样就和原意相符合了。

 
 
 
 

Linux内核中的cmpxchg函数的更多相关文章

  1. linux内核中的排序接口--sort函数

    linux内核中的sort函数,其实跟我们所说的qsort函数很像,我们来看看qsort: qsort 的函数原型是 void qsort(void*base,size_t num,size_t wi ...

  2. Linux 编程中的API函数和系统调用的关系【转】

    转自:http://blog.chinaunix.net/uid-25968088-id-3426027.html 原文地址:Linux 编程中的API函数和系统调用的关系 作者:up哥小号 API: ...

  3. Linux内核中常见内存分配函数【转】

    转自:http://blog.csdn.net/wzhwho/article/details/4996510 1.      原理说明 Linux内核中采用了一种同时适用于32位和64位系统的内存分页 ...

  4. (笔记)Linux内核中内存相关的操作函数

    linux内核中内存相关的操作函数 1.kmalloc()/kfree() static __always_inline void *kmalloc(size_t size, gfp_t flags) ...

  5. Linux内核中常见内存分配函数

    1.      原理说明 Linux内核中采用了一种同时适用于32位和64位系统的内存分页模型,对于32位系统来说,两级页表足够用了,而在x86_64系统中,用到了四级页表,如图2-1所示.四级页表分 ...

  6. Linux内核中常见内存分配函数(一)

    linux内核中采 用了一种同时适用于32位和64位系统的内存分页模型,对于32位系统来说,两级页表足够用了,而在x86_64系 统中,用到了四级页表. * 页全局目录(Page Global Dir ...

  7. linux内核中打印栈回溯信息 - dump_stack()函数分析【转】

    转自:http://blog.csdn.net/jasonchen_gbd/article/details/45585133 版权声明:本文为博主原创文章,转载请附上原博链接.   目录(?)[-] ...

  8. Linux内核中的算法和数据结构

    算法和数据结构纷繁复杂,但是对于Linux Kernel开发人员来说重点了解Linux内核中使用到的算法和数据结构很有必要. 在一个国外问答平台stackexchange.com的Theoretica ...

  9. [翻译] Linux 内核中的位数组和位操作

    目录 Linux 内核里的数据结构 原文链接与说明 Linux 内核中的位数组和位操作 位数组声明 体系结构特定的位操作 通用位操作 链接 Linux 内核里的数据结构 原文链接与说明 https:/ ...

随机推荐

  1. luogu P5328 [ZJOI2019]浙江省选

    传送门 每个人都可以看成一条直线\(y=ax+b\),所以我们要求的是每条线在整点处,上方线的数量的最小值(注意多条直线如果交于同一整点互不影响) 如果\(m=1\),其实只要求出半平面交,然后在半平 ...

  2. 集合类Hash Set,LinkedHashSet,TreeSet

    集合(set)是一个用于存储和处理无重复元素的高效数据结构.映射表(map)类似于目录,提供了使用键值快速查询和获取值的功能. HashSet类是一个实现了Set接口的具体类,可以使用它的无参构造方法 ...

  3. 查询GC日志、动态年龄计算

    https://www.cnblogs.com/onmyway20xx/p/6590603.html 从实际案例聊聊Java应用的GC优化 动态年龄计算 关于上文中提到晋升年龄阈值为2,很多同学有疑问 ...

  4. ArrayList知识详解

    简介 ArrayList是Java集合常用的数据结构之一,继承自AbstractList,实现了List,RandomAccess.Cloneable.Serializable等一系列接口,支持快速访 ...

  5. thinkphp 静态缓存设置

    'HTML_CACHE_RULES'=> array('ActionName' => array('静态规则', '静态缓存有效期', '附加规则'),'ModuleName(小写)' = ...

  6. [工具] BurpSuite--XssValidator插件

    0x00 安装 所需软件: 1.burpsuite 2.xssvalidator 源码:https://github.com/nVisium/xssValidator(按照编译指导编译) burpsu ...

  7. samba服务和client挂载

    服务端 1.安装samba服务 yum -y install samba 2.创建系统用户 因为Samba 服务程序的数据库要求账户必须在当前系统中已经存在,否则日后创建文件时将导致文件的权限属性混乱 ...

  8. IPython的简单介绍

    量化投资与Python 目录: 一.量化投资第三方相关模块 NumPy:数组批量计算 Pandas:表计算与数据分析 Matplotlib:图表绘制 二.IPython的介绍 IPython:和Pyt ...

  9. ESP-8266 串口通信(Serial)

    ESP8266的串口通信与传统的Arduino设备完全一样.除了硬件FIFO(128字节用于TX和RX)之外,硬件串口还有额外的 256字节的TX和RX缓存.发送和接收全都由中断驱动.当FIFO/缓存 ...

  10. 在Tomcat中部署Java Web应用程序几种方式

    在Tomcat中部署Java Web应用程序有两种方式:静态部署和动态部署.在下文中$CATALINA_HOME指的是Tomcat根目录. 一.静态部署      静态部署指的是我们在服务器启动之前部 ...