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)实现:

/* 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的内嵌汇编格式是

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

时间: 2024-10-11 00:24:14

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

linux kernel crash问题分析解决

一,问题场景和环境 系统环境: redhat6.4 kernel:2.6.32-358 问题: 使用iptables给mangle表添加了一条规则,使用nfqueue做为target.当一个http请求命中这个规则之后,机器直接重启了.偶发性的出了两次问题,但是却在重启的机器上重现不了这个问题. 二,排查 1,查看messages,kernel和dmesg相关日志,未发现有任何异常 2,查看重启前机器的负载,cpu,内存,磁盘io,网络io都正常 3,由于是使用了nfqueue做为target才

Linux Kernel中函数命名

在Linux Kernel中某些特定含义的函数命令有自己的规律,比如这里将简单的介绍一下函数名前双下划线__和devm_xxx函数的意思. 函数名称前的双下划线__ 标记需要加锁: 在内核中,有很多函数,有的是需要调用者自己加锁保护的,有些是不需要加锁保护的.对于这些场景,linux kernel采取了统一的策略:基本函数名字是一样的,只不过需要调用者自己加锁保护的那个函数需要增加__的前缀,例如内核有有下面两个函数:setup_irq()和__setup_irq(). 两个函数在kernel/

linux kernel input 子系统分析

Linux 内核为了处理各种不同类型的的输入设备 , 比如说鼠标 , 键盘 , 操纵杆 , 触摸屏 , 设计并实现了一个对上层应用统一的试图的抽象层 , 即是Linux 输入子系统 . 输入子系统的层次结构体如下 从底层到上层 , input 子系统由 设备驱动层 , 核心层 , 以及事件处理层3个部分组成 当一个鼠标移动, 一个按键按下或弹起 , 它都需要从底层设备驱动-->核心层-->事件处理层 -->用户空间 , 层层上报 , 一直到运用程序. 应用这个input  子系统有如下优

Arm Linux Kernel 构建 情景分析

概述 构建一个内核,一般是先配置,后编译.这里以构建 Nexus5 内核为例,代号为 hammerhead. 配置 通常做法是以厂商预置的配置为基础,根据自己需要进行配置.命令: make ARCH=arm hammerhead_defconfig 执行完毕后,"arch/arm/configs/hammerhead_defconfig" 文件会被复制到 ".config" ,作为默认配置. 然后运行以下命令根据自己需要进行配置: make ARCH=arm men

Linux Kernel Netfilter Helper 分析

linux netfilter 与helper相关的hook: 点击(此处)折叠或打开 { .hook = ipv4_conntrack_in, .owner = THIS_MODULE, .pf = PF_INET, .hooknum = NF_IP_PRE_ROUTING, .priority = NF_IP_PRI_CONNTRACK, } { .hook = ipv4_conntrack_help, .owner = THIS_MODULE, .pf = PF_INET, .hooknu

linux kernel 字符设备详解

有关Linux kernel 字符设备分析: 参考:http://blog.jobbole.com/86531/ 一.linux kernel 将设备分为3大类,字符设备,块设备,网络设备. 字符设备是指只能一个字节一个字节读写的设备, 常见的外设基本上都是字符设备. 块设备:常见的存储设备,硬盘,SD卡都归为块设备,块设备是按一块一块读取的. 网络设备:linux 将对外通信的一个机制抽象成一个设备, 通过套接字对其进行相关的操作. 每一个字符设备或块设备都在/dev目录下对应一个设备文件.l

Linux内核源码分析--内核启动之(5)Image内核启动(rest_init函数)(Linux-3.0 ARMv7)【转】

原文地址:Linux内核源码分析--内核启动之(5)Image内核启动(rest_init函数)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://blog.chinaunix.net/uid-25909619-id-4938395.html 前面粗略分析start_kernel函数,此函数中基本上是对内存管理和各子系统的数据结构初始化.在内核初始化函数start_kernel执行到最后,就是调用rest_init函数,这个函数的主要使命就是创建并启动内核线

Linux内核源码分析--内核启动之(6)Image内核启动(do_basic_setup函数)(Linux-3.0 ARMv7)【转】

原文地址:Linux内核源码分析--内核启动之(6)Image内核启动(do_basic_setup函数)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://blog.chinaunix.net/uid-25909619-id-4938396.html 在基本分析完内核启动流程的之后,还有一个比较重要的初始化函数没有分析,那就是do_basic_setup.在内核init线程中调用了do_basic_setup,这个函数也做了很多内核和驱动的初始化工作,详解

Linux内核源码分析--内核启动之(4)Image内核启动(setup_arch函数)(Linux-3.0 ARMv7)【转】

原文地址:Linux内核源码分析--内核启动之(4)Image内核启动(setup_arch函数)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://blog.chinaunix.net/uid-25909619-id-4938393.html 在分析start_kernel函数的时候,其中有构架相关的初始化函数setup_arch. 此函数根据构架而异,对于ARM构架的详细分析如下: void __init setup_arch(char **cmdlin