ITPub博客

TAS 指令与PostgreSQL spin lock

原创 国内数据库 作者:jesselyu 时间:2015-04-06 14:08:57 0 删除 编辑

    TAS: 指Test-And-Set,它是一个原子操作,修改内存的值,并返回原来的值。当一个进程P1对一个内存位置做TAS操作,不允许其它进程P2对此内存位置,再做TAS操作。

P2必须等P1操作完成后,再做TAS操作。以下是一个简单的锁,通过TAS来实现:



volatile int lock = 0;
void Critical() {
    while (TestAndSet(&lock) == 1);
    critical section // only one process can be in this section at a time 
    lock = 0 // release lock when finished with the critical section 
假设lock原来的值为“0”,当P1去做申请lock时,能获取得到锁。而此时P2再去申请锁时,必须spin,因为此时lock的值已经被P1修改为“1”了。

用TAS来实现spin lock,此处要注意volatile的使用。volatile表示这个变量是易失的,所以会编译器会每次都去内存中取原始值,而不是直接拿寄存器中的值。
这避免了在多线程编程中,由于多个线程更新同一个变更,内存中和寄存器中值的不同步而导致变量的值错乱的问题。另外,也会影响编译器的优化行为。


在PostgreSQL中,spin lock的实现包含在spin.c和s_lock.c两个文件中。在不支持TAS的情况下,PG会使用PGSemaphores来实现 spin lock。
 
1.使用semaphore实现 
其中spin.c主要封装了spin lock用PGSemaphores来实现的接口,跟硬件保持独立。PGSemaphore是使用OS底层的semaphore来实现的,PG对其做了封装,提供了PG系统内部统一的semaphore操作接口。
PG的用PGSemaphore结构体表示PG 自身的semaphore信号,并将相关操作封装在sembuf中,传递给底层OS。
PG semaphore lock操作:


PG semaphore结构体:
操作封装:
semop操作:

底层OS实现调用,OS声明在/usr/sys/include/sem.h中 
 



从上面可以看出,PG的semaphore实现非常清晰,而且与OS底层的调用关系也很明了。
 
2.使用TAS指令实现 
而s_lock.c 则用TAS方式了spin lock,与硬件相关。在PG源码中,使用s_lock函数来申请spin lock:
int s_lock(volatile slock_t *lock, const char *file, int line)
tas操作如下:
 


PG使用了自适应算法,来决定spin的次数和每次spin后,sleep的时间。

spins_per_delay:spin多少次后,开始sleep。默认为100,最大值为1000,最小值为10。

cur_delay:当前sleep的时间,最大值为1000,最小值为1。单位为毫秒。

spins_per_delay的值,基本上不变;但是cur_delay的值为当前值1倍和2倍之间变动。因此,spin delay次数越多,sleep时间会越长。

 

 

 

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/30088583/viewspace-1512121/,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论
资深Oracle RAC及Exadata部署实施运维专家,曾从事PostgreSQL内核开发与性能优化。丰富的大规模数据库管理架构设计经验。目前在阿里巴巴从事数据库架构工作以及新技术引进,对分布式存储,云计算及并行编程有一定研究。技术讨论请加微信:ljs521688

注册时间:2015-01-01

  • 博文量
    41
  • 访问量
    506544