TAS 指令与PostgreSQL spin lock-jesselyu
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 //> }假设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时间会越长。
页:
[1]