PostgreSQL V9.6 LWLock实现分析(四)

LWLock封锁的本质

    LWLock的锁操作,符合锁操作的技术本质,加锁同样是设置一个特定的标志位,释放锁是取消标识位。如下以CheckPoint相关操作为例,进行说明,其他类似。

    PostgreSQL定义LWLock结构体如下:

typedef struct LWLock  //LWLock的数据结构,绑定了锁、锁的属主、锁的等待者这三者,PostgreSQL 9.4.x之前的版本的实现方式有较大不同

{

    uint16        tranche;        /* tranche ID */  //LWLock上多了事务号,表明实施加锁操作的属主

    pg_atomic_uint32    state;    /* state of exclusive/nonexclusive lockers */  //锁对象,一个标志对象,有多个标志位(通常是无符号32位数),即有32个标志位,不同标志位表达不同含义。之所以这样定义,是因为这样的锁需要同时表达多种含义(如设置为LW_FLAG_HAS_WAITERS同时还可设置为)

    dlist_head    waiters;        /* list of waiting PGPROCs */  //本锁的等待队列,表明那些进程在等待本锁释放

#ifdef LOCK_DEBUG

    pg_atomic_uint32 nwaiters;    /* number of waiters */ //等待者的数量

    struct PGPROC *owner;         /* last exclusive owner of the lock */  //排它锁的属主

#endif

} LWLock;

    PostgreSQL定义了不同的标识符号,用以为“pg_atomic_uint32    state”标识不同的含义,已经在PostgreSQL中定义好的标识符号如下:

#define LW_FLAG_HAS_WAITERS    ((uint32) 1 << 30) //标志,LWLock锁有等待者

#define LW_FLAG_RELEASE_OK     ((uint32) 1 << 29) //标志,LWLock锁空闲,即没有施加锁(state值为“536870912”)

#define LW_FLAG_LOCKED         ((uint32) 1 << 28) //标志,LWLock锁存在,即已被加锁(state值为“268435456”)

 

#define LW_VAL_EXCLUSIVE       ((uint32) 1 << 24)  //锁的模式,LWLock排它锁模式,加锁的标志位之一,供加锁操作时使用

#define LW_VAL_SHARED          1                   //锁的模式,LWLock共享锁模式,加锁的标志位之一,供加锁操作时使用

#define LW_LOCK_MASK           ((uint32) ((1 << 25)-1)) //标志,LWLock锁标志

/* Must be greater than MAX_BACKENDS - which is 2^23-1, so we're fine. */

#define LW_SHARED_MASK         ((uint32) ((1 << 24)-1))  //标志,LWLock存在共享锁

    PostgreSQL定义LWLock结构体之后,一个LWLock对象就可以出现在其他需要被保护的对象中,如进程对象(PGPROC)。

struct PGPROC  //进程的结构体,PostgreSQL是多进程结构

{...

    /* Per-backend LWLock.  Protects fields below (but not group fields). */

    LWLock        backendLock;  //定义LWLock锁,用于保护进程结构体中的共享资源不受并发操作破坏

    //如下对象被backendLock保护

    /* Lock manager data, recording fast-path locks taken by this backend. */ 

    uint64        fpLockBits;        /* lock modes held for each fast-path slot */

    Oid           fpRelId[FP_LOCK_SLOTS_PER_BACKEND];        /* slots for rel oids */

    bool          fpVXIDLock;        /* are we holding a fast-path VXID lock? */

    LocalTransactionId fpLocalTransactionId;    /* lxid for fast-path VXID lock */

...};

    进程对象(PGPROC)通过InitProcGlobal()函数完成初始化,然后由此函数调用LWLockInitialize()函数完成对进程锁“backendLock”的初始化工作,即设置锁标志为 “LW_FLAG_RELEASE_OK”。

void

InitProcGlobal(void)

{...

    for (i = 0; i < TotalProcs; i++)

    {

      /*

         * Set up per-PGPROC semaphore, latch, and backendLock. Prepared xact

         * dummy PGPROCs don't need these though - they're never associated

         * with a real process

         */

        if (i < MaxBackends + NUM_AUXILIARY_PROCS)

        {

            PGSemaphoreCreate(&(procs[i].sem));

            InitSharedLatch(&(procs[i].procLatch));

            LWLockInitialize(&(procs[i].backendLock), LWTRANCHE_PROC);  //调用LWLockInitialize()初始化backendLock

        }

...

    }

...

}

    LWLockInitialize()函数要给“state”成员设置标识位为“LW_FLAG_RELEASE_OK”。

void

LWLockInitialize(LWLock *lock, int tranche_id)

{

    pg_atomic_init_u32(&lock->state, LW_FLAG_RELEASE_OK);  //设置无锁标志

#ifdef LOCK_DEBUG

    pg_atomic_init_u32(&lock->nwaiters, 0);

#endif

    lock->tranche = tranche_id;

    dlist_init(&lock->waiters);

}

    从以上的分析可以看出, PGPROC 结构体的初始化,是通过 LWLockInitialize() 函数在共享内存中对“ backendLock ”等完成,而“ backendLock ”的“ state ”是位于内存中的一块区域(无符号 32 个 bit 位的一段内存空间),这块区域用于标识是否用以保护 PGPROC 结构体。也就是说, LWLock 锁是内存中的一块区域,被用于置位,只是与 SpinLock 不同的是, LWLock 锁这块内存区域在不同的 bit 位设置不同的值以表示多种含义。另外 , 每个 LWLock 中有明确的属主和等待者,这起到了连接已加锁和申请加锁两种角色的作用,便于知晓谁在什么锁上等待谁这样的关系。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值