PostgreSQL启动过程中的那些事七:初始化共享内存和信号八:shmem中初始化常规锁管理器

 

       这一节 pg 初始化锁管理器,通过 InitLocks 例程实现,主要是创建了三个哈希表 第一个哈希表"LOCK hash" 用于管理锁,第二个哈希表"PROCLOCK hash" 用于管理进程锁,第三个"LOCALLOCK hash" 用于管理本地锁信息。其中第一个和第二个哈希表都是共享哈希表,第三个是非关系哈希表。初始化第三个哈希表"LOCALLOCK hash" 时在共享内存哈希表索引" ShmemIndex " 里没有创建索引,因为这个哈希表不在共享内存里,而是在MemoryContext "LOCALLOCK hash" 里分配的内存。

pg 中的锁有三种类型:自旋 锁(spinlock )、轻量锁(LWLock )、常规锁(Lock ), 作为一个主题另行讨论。

 

1 先上个图,看一下函数调用过程梗概,中间略过部分细节

 


PostgreSQL启动过程中的那些事七:初始化共享内存和信号八:shmem中初始化常规锁管理器_第1张图片

初始化 Lockmgr 方法调用流程图

 

2 初始化 xlog 相关结构

话说 main()-> ->PostmasterMain()-> ->reset_shared() -> CreateSharedMemoryAndSemaphores()> ->InitLocks() ,在 shmem 里分配了三个哈希表 第一个哈希表"LOCK hash" 用于管理锁,第二个哈希表"PROCLOCK hash" 用于管理进程锁,第三个"LOCALLOCK hash" 用于管理本地锁信息。其中第一个和第二个哈希表都是共享哈希表,第三个是非关系哈希表。初始化第三个哈希表"LOCALLOCK hash" 时在共享内存哈希表索引" ShmemIndex " 里没有创建索引,因为这个哈希表不在共享内存里,而是在MemoryContext "LOCALLOCK hash" 里分配的内存。

InitLocks()->ShmemInitHash ()->ShmemInitStruct() 在其中 调用 hash_search() 在哈希表索引 "ShmemIndex" 中查找 "LOCK hash" ,如果没有,就在 shmemIndex 中给 "LOCK hash" 分一个 HashElement ShmemIndexEnt entry ,在其中的 Entry 中写上 "LOCK hash" 。返回 ShmemInitStruct() ,再调用 ShmemAlloc() 在共享内存上给 "LOCK hash" 相关结构(见下面 "LOCK hash" 相关结构图 )分配空间,设置 entry (在这儿即ShmemIndexEnt 类型变量)的成员 location 指向该空间, size 成员记录该空间大小,然后返回 ShmemInitHash() ,调用 hash_create() ,创建哈希表 "LOCK hash" ,最后返回 ShmemInitHash() ,让 HTAB * 类型静态 全局变量 LockMethodLockHash 指向 哈希表 "LOCK hash"

接着 InitLocks()->ShmemInitHash ()->ShmemInitStruct() 在其中 调用 hash_search() 在哈希表索引 "ShmemIndex" 中查找 "PROCLOCK hash" ,如果没有,就在 shmemIndex 中给 "PROCLOCK hash" 分一个 HashElement ShmemIndexEnt entry ,在其中的 Entry 中写上 "PROCLOCK hash" 。返回 ShmemInitStruct() ,再调用 ShmemAlloc() 在共享内存上给 "PROCLOCK hash" 相关结构(见下面 "PROCLOCK hash" 相关结构图 )分配空间,设置 entry (在这儿即ShmemIndexEnt 类型变量)的成员 location 指向该空间, size 成员记录该空间大小,然后返回 ShmemInitHash() ,调用 hash_create() ,创建哈希表 "PROCLOCK hash" ,最后返回 ShmemInitHash() ,让 HTAB * 类型静态 全局变量 LockMethodProcLockHash 指向 哈希表 "PROCLOCK hash"

接着 InitLocks()->hash_create() 在其中 调用 AllocSetContextCreate() ,创建 MemoryContext "LOCALLOCK hash" (做个回顾,当前 MemoryContext 见下面的 当前 MemoryContext 结构图 ),调用 DynaHashAlloc() ,在 MemoryContext "LOCALLOCK hash" 上分配空间,创建 哈希表索引 "LOCALLOCK hash" (见下面 "LOCALLOCK hash" 相关结构图 ,最后返回 InitLocks() ,让 HTAB * 类型静态 全局变量 LockMethodLocalHash 指向 哈希表 "LOCALLOCK hash"

相关变量、结构定义和 初始化完成后数据结构图在下面。

LOCKTAG 结构被定义用于填充 16 字节。 请注意,如果 pg 要扩大 OID BlockNumber ,或 TransactionId 超过 32 位,这将需要调整。

       LOCKTAG 包含 lockmethodid 是为了共享内存里一个哈希表能够存储不同 lockmemthods 的锁。

typedef struct LOCKTAG

{

    uint32      locktag_field1; /* a 32-bit ID field */

    uint32      locktag_field2; /* a 32-bit ID field */

    uint32      locktag_field3; /* a 32-bit ID field */

    uint16      locktag_field4; /* a 16-bit ID field */

    uint8       locktag_type;   /* see enum LockTagType */

    uint8       locktag_lockmethodid;   /* lockmethod indicator */

} LOCKTAG;

 

每个被锁对象的锁信息:

tag :可锁对象的唯一标识符

grantMask :目前授予该对象的所有类型锁的位掩码

/*

  * Per-locked-object lock information:

  *

  * tag -- uniquely identifies the object being locked

  * grantMask -- bitmask for all lock types currently granted on this object.

  * waitMask -- bitmask for all lock types currently awaited on this object.

  * procLocks -- list of PROCLOCK objects for this lock.

  * waitProcs -- queue of processes waiting for this lock.

  * requested -- count of each lock type currently requested on the lock

  *      (includes requests already granted!!).

  * nRequested -- total requested locks of all types.

  * granted -- count of each lock type currently granted on the lock.

  * nGranted -- total granted locks of all types.

  *

  * Note: these counts count 1 for each backend.  Internally to a backend,

  * there may be multiple grabs on a particular lock, but this is not reflected

  * into shared memory.

  */

typedef struct LOCK

{

    /* hash key */

    LOCKTAG     tag;            /* unique identifier of lockable object */

 

    /* data */

    LOCKMASK    grantMask;      /* bitmask for lock types already granted */

    LOCKMASK    waitMask;       /* bitmask for lock types awaited */

    SHM_QUEUE   procLocks;      /* list of PROCLOCK objects assoc. with lock */

    PROC_QUEUE  waitProcs;      /* list of PGPROC objects waiting on lock */

    int          requested[MAX_LOCKMODES];       /* counts of requested locks */

    int          nRequested;     /* total of requested[] array */

    int          granted[MAX_LOCKMODES]; /* counts of granted locks */

    int          nGranted;       /* total of granted[] array */

} LOCK;

 

pg 可以有多个不同的 backend 进程在同一个开锁对象上持有或等待锁。 pg 需要为每个持有者 / 等待着存储一些信息。这些保存在结构 PROCLOCK 里。

PROCLOCKTAG 是在 proclock 哈希表里查找一个 PROCLOCK 项的关键信息。一个 PROCLOCKTAG 值唯一的标识一个可锁对象和一个持有者 / 等待着的组合。(这儿 pg 能使用指针,因为 PROCLOCKTAG 仅需要在 PROCLOCK 的生命周期里唯一,且不会在 lock proc 生存期以为)

为了不同的目的, backend 进程可以持有同一个锁:独立于会话锁, backend 进程跟踪事务锁。但是,这个在共享内存状态中没有反映出来: pg 仅跟踪持有锁的 backend 进程。这是可以的,因为 backend 进程不能阻塞自己。

holdMask 字段显示已经授予的由 proclock 代表的锁。注意,可能有一个具有 0 holdMask proclock 对象,对于任何锁,进程当前正在等待它。负责, holdMask 0 proclock 对象在方便的时候被尽快回收。

 

* releaseMask is workspace for LockReleaseAll(): it shows the locks due

  * to be released during the current call.  This must only be examined or

  * set by the backend owning the PROCLOCK.

每一个 PROCLOCK 对象被链接到链表,为了相关 LOCK 对象和所属 PGPROC 对象。注意, PROCLOCK 对象一被创建就就加入到这些链表,甚至还没有锁 lock 被授予的时候。等待 lock 锁被授予的 PGPROC 进程也会被链接到锁的等待进程( waitProcs )队列。

 

typedef struct PROCLOCKTAG

{

    /* NB: we assume this struct contains no padding! */

    LOCK       *myLock;         /* link to per-lockable-object information */

    PGPROC     *myProc;         /* link to PGPROC of owning backend */

} PROCLOCKTAG;

 

typedef struct PROCLOCK

{

    /* tag */

    PROCLOCKTAG tag;            /* unique identifier of proclock object */

 

    /* data */

    LOCKMASK    holdMask;       /* bitmask for lock types currently held */

    LOCKMASK    releaseMask;    /* bitmask for lock types to be released */

    SHM_QUEUE   lockLink;       /* list link in LOCK's list of proclocks */

    SHM_QUEUE   procLink;       /* list link in PGPROC's list of proclocks */

} PROCLOCK;

 

每一个backend 进程还维持一个本地哈希表,其记录着目前感兴趣的每一个锁lock 的信息。特别的,本地表记录着获得的这些锁的时间。这允许不额外访问共享内存的情况下对同一个锁多请求被执行。为了pg 能释放属于某个特别资源属主(ResourceOwner) 的锁。pg 还跟踪每个资源属主(ResourceOwner) 获得的锁数

typedef struct LOCALLOCKTAG

{

    LOCKTAG     lock;           /* identifies the lockable object */

    LOCKMODE    mode;           /* lock mode for this table entry */

} LOCALLOCKTAG;

 

typedef struct LOCALLOCKOWNER

{

    /*

      * Note: if owner is NULL then the lock is held on behalf of the session;

      * otherwise it is held on behalf of my current transaction.

      *

      * Must use a forward struct reference to avoid circularity.

      */

    struct ResourceOwnerData *owner;

    int64 nLocks;           /* # of times held by this owner */

} LOCALLOCKOWNER;

 

typedef struct LOCALLOCK

{

    /* tag */

    LOCALLOCKTAG tag;           /* unique identifier of locallock entry */

 

    /* data */

    LOCK       *lock;           /* associated LOCK object in shared mem */

    PROCLOCK   *proclock;       /* associated PROCLOCK object in shmem */

    uint32      hashcode;       /* copy of LOCKTAG's hash value */

    int64            nLocks;         /* total number of times lock is held */

    int          numLockOwners;  /* # of relevant ResourceOwners */

    int          maxLockOwners;  /* allocated size of array */

    LOCALLOCKOWNER *lockOwners; /* dynamically resizable array */

} LOCALLOCK;

 

#define LOCALLOCK_LOCKMETHOD(llock) ((llock).tag.lock.locktag_lockmethodid)

 


PostgreSQL启动过程中的那些事七:初始化共享内存和信号八:shmem中初始化常规锁管理器_第2张图片

 

初始化完 LOCK 相关结构 的内存结构图

       为了精简上图,把创建 shmem 的哈希表索引 "ShmemIndex" 时创建的 HCTL 结构删掉了,这个结构的作用是记录创建可扩展哈希表的相关信息。增加了左边灰色底的部分,描述 共享内存 /shmem 里各变量物理布局概览,由下往上,由低地址到高地址。 图中黄色的索引项就是本节新增加的索引项。 其中的 " LOCK hash " 相关结构内存图、 " PROCLOCK hash " 相关结构内存 图下面分别给出,要不上面的图太大太复杂了。在MemoryContext " PROCLOCK hash " 中分配的哈希表 " PROCLOCK hash " 相关结构内存图和当前pg中的 MemoryContext 相关实例图也在下边一并给出。


PostgreSQL启动过程中的那些事七:初始化共享内存和信号八:shmem中初始化常规锁管理器_第3张图片

"LOCK hash" 相关结构图

 


PostgreSQL启动过程中的那些事七:初始化共享内存和信号八:shmem中初始化常规锁管理器_第4张图片

"PROCLOCK hash" 相关结构图

 


PostgreSQL启动过程中的那些事七:初始化共享内存和信号八:shmem中初始化常规锁管理器_第5张图片

"LOCALLOCK hash" 相关结构图


PostgreSQL启动过程中的那些事七:初始化共享内存和信号八:shmem中初始化常规锁管理器_第6张图片

当前 pg 中的 MemoryContext 结构图

 

你可能感兴趣的:(Lock,PostgreSQL,内存管理,memorycontext,锁管理)