这一节 pg 初始化锁管理器,通过 InitLocks 例程实现,主要是创建了三个哈希表 。 第一个哈希表"LOCK hash" 用于管理锁,第二个哈希表"PROCLOCK hash" 用于管理进程锁,第三个"LOCALLOCK hash" 用于管理本地锁信息。其中第一个和第二个哈希表都是共享哈希表,第三个是非关系哈希表。初始化第三个哈希表"LOCALLOCK hash" 时在共享内存哈希表索引" ShmemIndex " 里没有创建索引,因为这个哈希表不在共享内存里,而是在MemoryContext "LOCALLOCK hash" 里分配的内存。
pg 中的锁有三种类型:自旋 锁(spinlock )、轻量锁(LWLock )、常规锁(Lock ), 作为一个主题另行讨论。
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)
初始化完 LOCK 相关结构 的内存结构图
为了精简上图,把创建 shmem 的哈希表索引 "ShmemIndex" 时创建的 HCTL 结构删掉了,这个结构的作用是记录创建可扩展哈希表的相关信息。增加了左边灰色底的部分,描述 共享内存 /shmem 里各变量物理布局概览,由下往上,由低地址到高地址。 图中黄色的索引项就是本节新增加的索引项。 其中的 " LOCK hash " 相关结构内存图、 " PROCLOCK hash " 相关结构内存 图下面分别给出,要不上面的图太大太复杂了。在MemoryContext " PROCLOCK hash " 中分配的哈希表 " PROCLOCK hash " 相关结构内存图和当前pg中的 MemoryContext 相关实例图也在下边一并给出。
"LOCK hash" 相关结构图
"PROCLOCK hash" 相关结构图
"LOCALLOCK hash" 相关结构图
当前 pg 中的 MemoryContext 结构图