linux原子操作
来源:互联网 发布:tomcat运行php文件 编辑:程序博客网 时间:2024/05/29 09:12
以下内容有些参考自网络,自然也将其学习所得的一点知识奉还给网络。
一 简介
linux文档中比较权威齐全的介绍位于Documentation/atomic_ops.txt,里面介绍的比较仔细全面,原子操作的由来主要为了解决现代操作系统多任务,多处理器并发的发展中资源竞争导致的同步问题,具体场景举例如下:
例如C语言语句“count++;”在未经编译器优化时生成的汇编代码为。
当操作系统内存在多个进程同时执行这段代码时,就可能带来并发问题。
假设count变量初始值为0。进程1执行完“mov eax, [count]”后,寄存器eax内保存了count的值0。此时,进程2被调度执行,抢占了进程1的CPU的控制权。进程2执行“count++;”的汇编代码,将累加后的count值1写回到内存。然后,进程1再次被调度执行,CPU控制权回到进程1。进程1接着执行,计算count的累加值仍为1,写回到内存。虽然进程1和进程2执行了两次“count++;”操作,但是count实际的内存值为1,而不是2!
那么如何解决这个问题呢?不同处理器架构对此都有不同的实现方法!!
二 实现
内核提供了一个特殊的类型atomic_t,具体定义如下:
typedef struct {
int counter;
} atomic_t;
从上面的定义来看,atomic_t实际上就是一个int类型的counter,不过定义这样特殊的类型atomic_t是有其思考的:内核定义了若干atomic_xxx的接口API函数,这些函数只会接收atomic_t类型的参数。这样可以确保atomic_xxx的接口函数只会操作atomic_t类型的数据。同样的,如果你定义了atomic_t类型的变量(你期望用atomic_xxx的接口API函数操作它),这些变量也不会被那些普通的、非原子变量操作的API函数(如初始化时用memset)接受。
具体的接口API函数整理如下:
接口函数
描述
static inline void atomic_add(int i, atomic_t *v)
给一个原子变量v增加i
static inline int atomic_add_return(int i, atomic_t *v)
同上,只不过将变量v的最新值返回
static inline void atomic_sub(int i, atomic_t *v)
给一个原子变量v减去i
static inline int atomic_sub_return(int i, atomic_t *v)
同上,只不过将变量v的最新值返回
static inline int atomic_cmpxchg(atomic_t *ptr, int old, int new)
比较old和原子变量ptr中的值,如果相等,那么就把new值赋给原子变量。
返回旧的原子变量ptr中的值
atomic_read
获取原子变量的值
atomic_set
设定原子变量的值
atomic_inc(v)
原子变量的值加一
atomic_inc_return(v)
同上,只不过将变量v的最新值返回
atomic_dec(v)
原子变量的值减去一
atomic_dec_return(v)
同上,只不过将变量v的最新值返回
atomic_sub_and_test(i, v)
给一个原子变量v减去i,并判断变量v的最新值是否等于0
atomic_add_negative(i,v)
给一个原子变量v增加i,并判断变量v的最新值是否是负数
static inline int atomic_add_unless(atomic_t *v, int a, int u)
只要原子变量v不等于u,那么就执行原子变量v加a的操作。
如果v不等于u,返回非0值,否则返回0值
X86原子操作实现
比如atomic_inc API具体实现,其他类似:
static inline void atomic_inc(atomic_t *v){asm volatile(LOCK_PREFIX "incl %0" : "+m" (v->counter));}
其中LOCK_PREFIX宏的定义为
#ifdef CONFIG_SMP#define LOCK_PREFIX_HERE \".pushsection .smp_locks,\"a\"\n"\".balign 4\n"\".long 671f - .\n" /* offset */\".popsection\n"\"671:"#define LOCK_PREFIX LOCK_PREFIX_HERE "\n\tlock; "#else /* ! CONFIG_SMP */#define LOCK_PREFIX_HERE ""#define LOCK_PREFIX ""#endif
可见,在对称多处理器架构的情况下,LOCK_PREFIX被解释为指令前缀lock。而对于单处理器架构,LOCK_PREFIX不包含任何内容.
Arm原子操作实现
在arm下(linux-3.10.x),atomic_inc展开为atomic_add(1, v),在armv6以上(armV6之前的不讨论,一般是屏蔽中断实现)的具体实现如下:
static inline void atomic_add(int i, atomic_t *v){unsigned long tmp;int result;__asm__ __volatile__("@ atomic_add\n""1:ldrex%0, [%3]\n""add%0, %0, %4\n""strex%1, %0, [%3]\n""teq%1, #0\n""bne1b": "=&r" (result), "=&r" (tmp), "+Qo" (v->counter): "r" (&v->counter), "Ir" (i): "cc");}
关于x86和arm架构的具体实现可以参考以下链接:
http://www.wowotech.net/kernel_synchronization/atomic.html
http://www.cnblogs.com/fanzhidongyzby/p/3654855.html
在arm下(linux-3.10.x),atomic_inc展开为atomic_add(1, v),在armv6以上(armV6之前的不讨论,一般是屏蔽中断实现)的具体实现如下:
static inline void atomic_add(int i, atomic_t *v){unsigned long tmp;int result;__asm__ __volatile__("@ atomic_add\n""1:ldrex%0, [%3]\n""add%0, %0, %4\n""strex%1, %0, [%3]\n""teq%1, #0\n""bne1b": "=&r" (result), "=&r" (tmp), "+Qo" (v->counter): "r" (&v->counter), "Ir" (i): "cc");}关于x86和arm架构的具体实现可以参考以下链接:
http://www.wowotech.net/kernel_synchronization/atomic.html
http://www.cnblogs.com/fanzhidongyzby/p/3654855.html
三 应用
初始化
初始化分为静态初始化和动态初始化两种。静态初始化类似:
static atomic_t probe_count = ATOMIC_INIT(0);
动态初始化则调用atomic_set接口进行变量的显示赋值:
atomic_set(&q->depth, 0);
加,减,读,测试
关于原子变量的主要运算操作,参考上表,语义较明显,不作过多阐述。
- Linux 原子操作
- Linux原子操作
- linux 原子操作
- linux 原子操作
- linux原子操作
- Linux 原子操作
- Linux 原子操作
- Linux 原子操作
- linux 原子操作
- linux中的原子操作
- 【转】Linux 原子操作
- Linux 原子操作
- linux gcc原子操作
- Linux 原子操作
- linux原子操作
- Linux Driver原子操作
- linux原子操作
- Linux 原子操作
- 理解strong与weak)强引用与弱引用转载自《http://blog.sina.com.cn/s/blog_7c8dc2d50101lc08.html》
- jQiery事件
- iOS 封装BaseModel
- app混淆
- Ubuntu下使用模拟器Bochs运行《Linux内核完全剖析——基于0.12内核》第3章P31 引导扇区源码boot.s的一系列步骤
- linux原子操作
- Java时间:GMT以及UTC
- PHP基于代码模板的编程思想
- rmmod卸载驱动模块时提示:No such file or directory
- 正向代理与反向代理的区别【Nginx读书笔记】
- 分析.pin文件时,用到的几个自定义函数
- web开发者
- QWeb Reports
- 大话Philm的从无到有(一)诞生篇