zoukankan      html  css  js  c++  java
  • 原子性操作原理分析

    1. 概念

    原子操作是指不被打断的操作,即它是最小的执行单位。最简单的原子操作就是一条条的汇编指令(不包括一些伪指令,伪指令会被汇编器解释成多条汇编指令)。在 linux 中原子操作对应的数据结构为 atomic_t,定义如下:

    typedef struct {
    	int counter;
    } atomic_t;
    

    本质上就是一个整型变量,之所以定义这么一个数据类型,是为了让原子操作函数只接受 atomic_t 类型的操作数,如果传入的不是 atomic_t 类型数据,在程序编译阶段就不会通过;另一个原因就是确保编译器不会对相应的值进行访问优化,确保对它的访问都是对内存的访问,而不是对寄存器的访问。

    2. 赋值操作

    ARM 处理器有直接对内存地址进行赋值的指令(STR)。

    #define atomic_set(v,i)	(((v)->counter) = (i))
    

    3. 读操作

    用 volatile 来防止编译器对变量访问的优化,确保是对内存的访问,而不是对寄存器的访问。

    #define atomic_read(v)	(*(volatile int *)&(v)->counter)
    

    4. 加操作

    使用独占指令完成累加操作。

    static inline void atomic_add(int i, atomic_t *v)
    {
    	unsigned long tmp;
    	int result;
    	// 使用独占指令读取,然后执行加操作,独占写失败时就重新执行
    	__asm__ __volatile__("@ atomic_add
    "
    "1:	ldrex	%0, [%3]
    "
    "	add	%0, %0, %4
    "
    "	strex	%1, %0, [%3]
    "
    "	teq	%1, #0
    "
    "	bne	1b"
    	: "=&r" (result), "=&r" (tmp), "+Qo" (v->counter)
    	: "r" (&v->counter), "Ir" (i)
    	: "cc");
    }
    

    5. 减操作

    对比加操作减操作的代码可以看出,它们非常的相似,其实不同的地方就一句,所以现在最新的内核源码中已经使用宏定义 ATOMIC_OP(op, c_op, asm_op) 来重写了这部分代码。

    static inline void atomic_sub(int i, atomic_t *v)
    {
    	unsigned long tmp;
    	int result;
    	// 使用独占指令读取,然后执行减操作,独占写失败时就重新执行
    	__asm__ __volatile__("@ atomic_sub
    "
    "1:	ldrex	%0, [%3]
    "
    "	sub	%0, %0, %4
    "
    "	strex	%1, %0, [%3]
    "
    "	teq	%1, #0
    "
    "	bne	1b"
    	: "=&r" (result), "=&r" (tmp), "+Qo" (v->counter)
    	: "r" (&v->counter), "Ir" (i)
    	: "cc");
    }
    

    6. 其他操作

    类似的原子操作函数还有一些,比如 atomic_XXX_return、atomic_cmpxchg、atomic_clear_mask,以及在此基础上实现的 atomic_inc、atomic_dec、atomic_XXX_and_test、atomic_XXX_return等。以上代码都是针对 SMP 处理器的实现方式,针对非 SMP 处理器,由于不存在其他核心的抢占,所以只需要防止其他进程抢占即可实现原子操作,例如加操作

    static inline int atomic_sub_return(int i, atomic_t *v)
    {
    	unsigned long flags;
    	int val;
    	// 通过关闭中断防止其他进程打断代码的执行
    	raw_local_irq_save(flags);
    	val = v->counter;
    	v->counter = val -= i;
    	// 恢复中断原始的状态
    	raw_local_irq_restore(flags);
    
    	return val;
    }
    

    7. 总结

    原子性操作的实现需要具体体系结构相关的指令集的支持。

  • 相关阅读:
    C# 委托事件
    用netstat查看网络状态详解
    详解TCP建立连接全过程
    Amazon SNS移动推送更新——新增百度云推送和Windows平台支持
    UE-9260使用说明2
    简单理解javascript中的原型对象,实现对之间共享属性和行为
    RxJava
    链式存储(头插法、尾插法)
    Codeforces 569 B. Inventory
    CentOS 6.4安装Puppet
  • 原文地址:https://www.cnblogs.com/jiau/p/13189253.html
Copyright © 2011-2022 走看看