TAS 指令與PostgreSQL spin lock

jesselyu發表於2015-04-06

    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/,如需轉載,請註明出處,否則將追究法律責任。

相關文章