同步

来源:互联网 发布:前端ajax请求数据 编辑:程序博客网 时间:2024/04/27 23:15

    使用锁和内存栅栏可以实现同步。锁包括读写锁rwlock、brlock(big reader lock)、spinlock、refcnt(引用计数和自旋锁一起应用)以及RCU(read-copy update)。

   brlock适用于读者多,写者少的情况。读者开销少,写者开销多。

   RCU是也是一种rwlock,八十年代已经出现,在二十世纪九十年代出现了一个比较高效的实现,而在linux中是在开发内核2.5.43中引入该技术的并正式包含在2.6内核中RCU克服以上锁的缺点。读者访问时不上锁,可以运行多个读者访问;写者访问,需要拷贝一份副本,进行修改后,在总线空闲,无读者访问那段RCU保护的数据时,通过回调函数将指向原来数据的指针重新指向新的被修改的数据。如果有多个写者,需要进行写者之间的同步。

    读者在访问被RCU保护的共享数据期间不能被阻塞,这是RCU机制得以实现的一个基本前提,也就说当读者在引用被RCU保护的共享数据期间,读者所 在的CPU不能发生上下文切换,spinlock和rwlock都需要这样的前提。

因为对于RCU,对共享数据的操作必须保证能够被没有使用同步机制的读者看到,即对共享数据的操作,所有读者都是可见的,所以内存栅是非常必要的。


内存栅栏(memory barrier)

    内存栅栏使得CPU的内存操作顺序优化和Cache优化不能发挥作用,内存栅栏使编译器确保对RAM中数据的改变对所有CPU都是可见的。x86上内核定义的内存屏障原语:

#define barrier() __asm__ __volatile__("": : :"memory") 
#define mb() alternative("lock; addl $0,0(%%esp)", "mfence", X86_FEATURE_XMM2) 
#define rmb() alternative("lock; addl $0,0(%%esp)", "lfence", X86_FEATURE_XMM2) 


 barrier()宏只约束gcc编译器,不约束运行时的CPU行为。

1 int a = 5, b = 6; 
2 barrier(); 
3 a = b; 

在line 3,GCC不会用存放b的寄存器给a赋值,而是invalidate(使无效) b的Cache line,重新读内存中的b值,赋值给a。 


lock前缀使得本地CPU的cache写入内存,引起其他CPU无效Cache Line(由于IA32在每个CPU内部实现了Snoopying(BUS-Watching)技术,监视着总线上是否发生了写内存操作(由某个CPU或DMA控制器发出的),只要发生了,就invalidate相关的Cache line。