发布者:上海IT外包来源:http://www.lanmon.net点击数:2071
蓝盟IT小贴士,来喽!
小粒度——指令
根据英特尔手册卷3第8章的说明,x86使用三种机制来实现原子操作。
1. Guaranteed atomic operations。 保障原子操作是保证原子性的基本读/写存储器操作。 通常,读写位于一个cache line上的数据是原子的。
2 .使用2. bus lock、LOCK#信号和命令的lock前缀。 锁定总线的方式很简单,进行原子操作的cpu在bus上assert lock #信号,这时其他cpu的操作住在block。
3. cache lock是使用cache性能协议(MESI协议)来实现的。 如果要访问的内存空间已经位于当前cpu的cache上,请使用cache一致性协议来实现原子操作。 否则,锁定公共汽车。
英特尔的初始cpu (例如英特尔386、英特尔486和奔腾处理器)通过bus lock实现原子操作。 这样实现的问题是,在完全不相关的两个cpu之间,总线锁也会竞争,整体性能会下降。 在随后的cpu中,intel优化了这个问题。 如果进行原子操作的内存已经引入cache,cpu将使用cache完整性协议保证原子性。 这叫做cache lock。 与bus lock相比,cache lock的粒度更细,可以得到更好的性能。
在x86中,有些指令具有lock语义,例如更新XCHG和段描述符。 其他命令可以手动添加lock前缀以实现lock语义,如BTS、BTR和CMPXCHG命令。 在这些命令中,最核心的是CAS(Compare And Swap )命令,是实现各种锁定语义的核心命令。 与具有原子意义的XCHG不同,CAS操作是以“lock CMPXCHG”的形式实现的。 通常,原子操作的数据长度不超过8字节,也不允许同时对两个存储器地址进行CAS操作(如果可能的话,无锁双向链表不是梦)。
原子操作的另一个话题是ABA问题,水平有限,所以谈不下去。 简单举个例子,在linux内核的slub实现中,不是同时CAS两个存储器地址的黑魔术,而是用CMPXCHG16B命令解决ABA问题的宏函数,感兴趣的可以深入。粒度大
如果原子操作的对象尺寸在16字节或8字节以内,则可以用1、2命令进行原子操作。 但是,在对象的大小较大的情况下,为了实现原子操作需要锁定和COW等其他方法。 深入研究这两种方法,本质上可以看出问题已转换成16字节的原子操作。
锁上。
上锁这种方式被很好地理解,上锁的话整个临界域的操作可以看作原子操作。
内核备有各种锁、自旋锁、读写锁、seq锁、mutex、semaphore等,这些锁根据领导的倾向而不同,取决于是否允许睡眠。
简单来说,自旋锁和读写锁的核心是利用原子指令CAS操作32位/64位的值,它们都不允许休眠,但读写锁对读者进行了优化,允许多个读者同时读取数据mutex和semaphore也是基于自旋锁实现的,但可以休眠互斥区域的操作。
上锁这种方式可以看出,最核心的是命令的原子操作。
COW
对大对象原子操作的另一种方法是COW(copy on write )。
cow的思想其实很简单,首先我们有这个大对象的指南。 如果需要原子上修改这个大对象的数据,因为无法进行inplace修改,所以复制这个对象的数据,用对象的副本进行修改,最后原子上修改这个对象的指针。 可以看出这里最核心的是利用命令实现指针的更换。
文/上海蓝盟 IT外包专家
分享到: