Linux的原子操作与同步机制
Linux的原子操作与同步机制
。进程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!
)从内存将count的数据读取到cpu。
2)累加读取的值。
3)将修改的值写回count内存。
这又回到前面并发问题类似的情况,只不过此时并发的主题不再是进程,而是处理器。
Intel x86指令集提供了指令前缀lock用于锁定前端串行总线(FSB),保证了指令执行时不会受到其他处理器的干扰。
使用lock指令前缀后,处理器间对count内存的并发访问(读/写)被禁止,从而保证了指令的原子性。
位的数据。
typedef struct { volatile int counter; } atomic_t;
其中原子操作函数atomic_inc完成自加原子操作。
/**
* atomic_inc - increment atomic variable
* @v: pointer of type atomic_t
*
* Atomically increments @v by 1.
*/
static __inline__ void atomic_inc(atomic_t *v)
{
__asm__ __volatile__(
LOCK "incl %0"
:"=m" (v->counter)
:"m" (v->counter));
}
其中LOCK宏的定义为。
#ifdef CONFIG_SMP
#define LOCK "lock ; "
#else
#define LOCK ""
#endif
可见,在对称多处理器架构的情况下,LOCK被解释为指令前缀lock。而对于单处理器架构,LOCK不包含任何内容。
,清除“Exclusive”标记,否则直接将temp置为1结束。
teq指令测试temp值是否为0。
bne指令temp不等于0时跳转到标号1,其中字符b表示向后跳转。
整体看来,上述汇编代码一直尝试完成“v->counter+=i”的操作,直到temp为0时结束。
使用ldrex和strex指令对是否可以保证add指令的原子性呢?假设两个进程并发执行“ldrex+add+strex”操作,当进程1执行ldrex后设定了全局标记“Exclusive”。此时切换到进程2,执行ldrex前全局标记“Exclusive”已经设定,ldrex执行后重复设定了该标记。然后执行add和strex指令,完成累加操作。再次切换回进程1,接着执行add指令,当执行strex指令时,由于“Exclusive”标记被进程2清除,因此不执行传送操作,将temp设置为1。后继teq指令测定temp不等于0,则跳转到起始位置重新执行,最终完成累加操作!可见ldrex和strex指令对可以保证进程间的同步。多处理器的情况与此相同,因为arm的原子操作只关心“Exclusive”标记,而不在乎前端串行总线是否加锁。
在ARMv6之前,swp指令就是通过锁定总线的方式完成原子的数据交换,但是影响系统性能。ARMv6之后,一般使用ldrex和strex指令对代替swp指令的功能。
:
lock decb [lock->slock]
jns 3
2:
rep nop
cmpb $0, [lock->slock]
jle 2
jmp 1
3:
其中lock->slock字段初始值为1,执行原子操作decb后值为0。符号位为0,执行jns指令跳转到3,完成自旋锁的加锁。
当再次申请自旋锁时,执行原子操作decb后lock->slock值为-1。符号位为1,不执行jns指令。进入标签2,执行一组nop指令后比较lock->slock是否小于等于0,如果小于等于0回到标签2进行循环(自旋)。否则跳转到标签1重新申请自旋锁,直到申请成功。
自旋锁释放时会将lock->slock设置为1,这样保证了其他进程可以获得自旋锁。
。如果该值减为负数(符号位为1)则跳转到另一个段内的标签2,否则申请信号量成功。
标签2被编译到另一个段内,进入标签2后,执行lea指令取出sem->count的地址,放到eax寄存器作为参数,然后调用函数__down_failed表示信号量申请失败,进程加入等待队列。最后跳回标签1结束信号量申请。
信号量的释放操作由函数up实现。
/*
* Note! This is subtle. We jump to wake people up only if
* the semaphore was negative (== somebody was waiting on it).
* The default case (no contention) will result in NO
* jumps for both down() and up().
*/
static inline void up(struct semaphore * sem)
{
__asm__ __volatile__(
"# atomic up operation\n\t"
LOCK "incl %0\n\t" /* ++sem->count */
"jle 2f\n"
"1:\n"
LOCK_SECTION_START("")
"2:\tlea %0,%%eax\n\t"
"call __up_wakeup\n\t"
"jmp 1b\n"
LOCK_SECTION_END
".subsection 0\n"
:"=m" (sem->count)
:
:"memory","ax");
}
实际的汇编代码形式为。
lock incl sem->count
jle 2
1:
<========== another section ==========>
2:
lea [sem->count], eax
call __up_wakeup
jmp 1
释放信号量时执行原子操作incl将sem->count加1,如果该值小于等于0,则说明等待队列有阻塞的进程需要唤醒,跳转到标签2,否则信号量释放成功。
标签2被编译到另一个段内,进入标签2后,执行lea指令取出sem->count的地址,放到eax寄存器作为参数,然后调用函数__up_wakeup唤醒等待队列的进程。最后跳回标签1结束信号量释放。
总结
本文通过对操作系统并发问题的讨论研究操作系统内的原子操作的实现原理,并讨论了不同体系结构下Linux原子操作的实现,最后描述了Linux操作系统如何利用原子操作实现常见的进程同步机制,希望对你有所帮助。
Linux的原子操作与同步机制的更多相关文章
- Linux驱动开发5——同步机制
上一章讲到了并发,指的是多个进程同时存取临界区资源的处理机制.这一章讲的同步机制,讲的是多个进程之间协同工作的处理机制,如临界区数据还没有准备好,A进程负责准备数据,B进程等待A进程完成之后读取数据. ...
- [内核同步]浅析Linux内核同步机制
转自:http://blog.csdn.net/fzubbsc/article/details/37736683?utm_source=tuicool&utm_medium=referral ...
- Linux内核同步机制
http://blog.csdn.net/bullbat/article/details/7376424 Linux内核同步控制方法有很多,信号量.锁.原子量.RCU等等,不同的实现方法应用于不同的环 ...
- Linux内核的同步机制
本文详细的介绍了Linux内核中的同步机制:原子操作.信号量.读写信号量和自旋锁的API,使用要求以及一些典型示例 一.引言 在现代操作系统里,同一时间可能有多个内核执行流在执行,因此内核其实象多进程 ...
- linux同步机制
很早之前就接触过同步这个概念了,但是一直都很模糊,没有深入地学习了解过,近期有时间了,就花时间研习了一下<linux内核标准教程>和<深入linux设备驱动程序内核机制>这两本 ...
- Linux内核同步机制--转发自蜗窝科技
Linux内核同步机制之(一):原子操作 http://www.wowotech.net/linux_kenrel/atomic.html 一.源由 我们的程序逻辑经常遇到这样的操作序列: 1.读一个 ...
- Linux 内核同步机制
本文将就自己对内核同步机制的一些简要理解,做出一份自己的总结文档. Linux内部,为了提供对共享资源的互斥访问,提供了一系列的方法,下面简要的一一介绍. Technorati 标签: ...
- 浅析Linux内核同步机制
非常早之前就接触过同步这个概念了,可是一直都非常模糊.没有深入地学习了解过,最近有时间了,就花时间研习了一下<linux内核标准教程>和<深入linux设备驱动程序内核机制>这 ...
- Linux 下的同步机制
2017-03-10 回想下最初的计算机设计,在单个CPU的情况下,同一时刻只能由一个线程(在LInux下为进程)占用CPU,且2.6之前的Linux内核并不支持内核抢占,当进程在系统地址运行时,能打 ...
随机推荐
- ZooKeerper学习之Watcher
ZooKeeper为我们提供了用于监视结点变化的Watcher机方法制: 1.可以注册Watcher的方法:getData().exists().getChildren().我们可以通过查看ZooKe ...
- Chrome 中的彩蛋,一款小游戏,你知道吗?
今天看到一篇文章,介绍chrome中的彩蛋,带着好奇心进去看了一眼,没想到发现了一款小游戏,个人觉得还不错,偶尔可以玩一下,放松放松心情!^_^ 当 Chrome 无法连接到互联网时, 或者上着网突然 ...
- JavaScript isNaN() 函数
定义与用法: isNaN() 函数用于检查其参数是否是非数字值. 语法: isNaN(x) 描述: x是要检测的值. 返回值: 如果 x 是特殊的非数字值 NaN(或者能被转换为这样的值),返 ...
- PDA手持扫描资产标签,盘点完成后将数据上传到PC端,固定资产系统查看盘点结果
固定资产管理系统介绍: 致力于研发条码技术.集成条码系统的专业性公司,针对客户的不同需求,提供一站式的企业条码系统解决方案:包括功能强大的软件系统.安全可靠的无线网络.坚固耐用的硬件系统.灵活易用的管 ...
- C++11 笔记
5.重载运算符 本质上是一个函数. 函数名为operator(+-*/--) 如果一个运算符是成员函数,其左侧运算对象就绑定到隐式的this参数上. a.拷贝赋值运算符 例如: class Foo { ...
- [NOIP2016]愤怒的小鸟 D2 T3 状压DP
[NOIP2016]愤怒的小鸟 D2 T3 Description Kiana最近沉迷于一款神奇的游戏无法自拔. 简单来说,这款游戏是在一个平面上进行的. 有一架弹弓位于(0,0)处,每次Kiana可 ...
- 使用js-xlsx库,前端读取Excel报表文件
在实际开发中,经常会遇到导入Excel文件的需求,有的产品人想法更多,想要在前端直接判断文件内容格式是否正确,必填项是否已填写 依据HTML5的FileReader,可以使用新的API打开本地文件(参 ...
- jQuery技巧
回到顶部按钮 图片预加载 判断图片是否加载完 自动修补破损图像 Hover切换class类 禁用输入 停止正在加载的链接 toggle fade/slide 简单的手风琴 使两个DIV同等高度 在浏览 ...
- 提交数据url太长导致提交失败
使用了dojo开发.在datagrid过滤的时候.为了让过滤好处理,直接设置为完全二叉树的方式来存查询条件.但是在提交数据的时候,默认是get?url的方式.结果导致条件选择一两个,url会特别长.然 ...
- 一张图系列——为什么在DllMain里面创建了线程并Wait会卡死
这是一个老话题了,推荐一篇文章: http://blog.csdn.net/breaksoftware/article/details/8150476#0-tsina-1-83826-39723281 ...