相关:
《Postgresql源码(40)Latch的原理分析和应用场景》
《Postgresql源码(67)LWLock锁的内存结构与初始化》
《Postgresql源码(68)virtualxid锁的原理和应用场景》
速查:
全局速查变量:
共享内存顶部的int4字节对齐到128字节上,剩下了124字节;且这个变量不常改变,不会造成cacheline miss,这124字节可以用来存点别的:)
Postgresql的LWLock体系整体有两部分组成:
main->PostmasterMain->process_shared_preload_libraries->load_libraries->load_file->internal_load_library->_PG_init->RequestNamedLWLockTranche)所有的锁有名字,PG中叫做TrancheNames。
以LWLockShmemSize函数为线索,总结下PG到底有多少锁。
PG14
/*
* Compute shmem space needed for LWLocks and named tranches.
*/
Size
LWLockShmemSize(void)
{
Size size;
int i;
int numLocks = NUM_FIXED_LWLOCKS;
/* Calculate total number of locks needed in the main array. */
numLocks += NumLWLocksForNamedTranches();
/* Space for the LWLock array. */
size = mul_size(numLocks, sizeof(LWLockPadded));
/* Space for dynamic allocation counter, plus room for alignment. */
size = add_size(size, sizeof(int) + LWLOCK_PADDED_SIZE);
/* space for named tranches. */
size = add_size(size, mul_size(NamedLWLockTrancheRequests, sizeof(NamedLWLockTranche)));
/* space for name of each tranche. */
for (i = 0; i < NamedLWLockTrancheRequests; i++)
size = add_size(size, strlen(NamedLWLockTrancheRequestArray[i].tranche_name) + 1);
/* Disallow adding any more named tranches. */
lock_named_request_allowed = false;
return size;
}
固定锁总数NUM_FIXED_LWLOCKS=208个,包含下面几类锁。
#define NUM_BUFFER_PARTITIONS 128
/* Number of partitions the shared lock tables are divided into */
#define LOG2_NUM_LOCK_PARTITIONS 4
#define NUM_LOCK_PARTITIONS (1 << LOG2_NUM_LOCK_PARTITIONS)
/* Number of partitions the shared predicate lock tables are divided into */
#define LOG2_NUM_PREDICATELOCK_PARTITIONS 4
#define NUM_PREDICATELOCK_PARTITIONS (1 << LOG2_NUM_PREDICATELOCK_PARTITIONS)
/* Offsets for various chunks of preallocated lwlocks. */
#define BUFFER_MAPPING_LWLOCK_OFFSET NUM_INDIVIDUAL_LWLOCKS
#define LOCK_MANAGER_LWLOCK_OFFSET \
(BUFFER_MAPPING_LWLOCK_OFFSET + NUM_BUFFER_PARTITIONS)
#define PREDICATELOCK_MANAGER_LWLOCK_OFFSET \
(LOCK_MANAGER_LWLOCK_OFFSET + NUM_LOCK_PARTITIONS)
#define NUM_FIXED_LWLOCKS \
(PREDICATELOCK_MANAGER_LWLOCK_OFFSET + NUM_PREDICATELOCK_PARTITIONS)
可读性太差,总结下:

调用API动态申请的锁,一般是给插件使用,因为插件的init函数会在PG主进程共享内存初始化前调用。如果已经走完共享内存初始化的流程,在申请锁就没有效果了。
RequestNamedLWLockTranche:【注册登记动态锁】共享内存初始化前,调用该函数把锁信息记录下来。注意可以这里可以注册多个内存连续的LWLock。共享内存初始化时InitializeLWLocks会把登记的锁和fixed锁一块初始化。GetNamedLWLockTranche:【根据注册名字拿锁】通过锁名字查找LWLock数组。如果注册了多个锁,这里返回第一把锁的指针,多把锁是内存连续存放的。NumLWLocksForNamedTranches:返回总锁个数。NamedLWLockTrancheRequests:申请Named动态锁的次数。NamedLWLockTrancheRequestArray:NamedLWLockTrancheRequest类型数组,每一次申请对应一个NamedLWLockTrancheRequest。NamedLWLockTrancheRequest结构包括:
char tranche_name[]:锁名(可能不止一把锁,所以也可能是一批锁的名字)。int num_lwlocks:本次申请锁的个数。两套锁机制初始化时是要放在一起初始化的。
内存结构第一部分都是用于动态锁的维护,记录了动态锁的数量。
内存结构第二部分是锁的控制结构,208把固定锁+动态锁,每一把锁对应一个LWLockPadded结构。
内存结构第三、四部分都是用于动态锁的维护,记录了动态锁的id和名字的关联、名称字符串。
LWLockCounter用来给动态锁赋ID,初始=72原因是固定锁虽然有208个,但只有72个名字,同名的锁会使用相同ID。
|--------------------------------------------------| <----- LWLockCounter(初始=72因为固定锁有72个名字,每次动态锁需要ID,+1使用)
| (一个int对齐LWLOCK_PADDED_SIZE,记录动态申请锁数量) | (对齐到128字节,int只有4字节,浪费124字节,int放在靠近高内存的位置)
|--------------------------------------------------| <----- MainLWLockArray
| |
| (固定锁208 + 动态锁数量) * sizeof(LWLockPadded) |
| |
|--------------------------------------------------| <----- NamedLWLockTrancheArray
| |
| (动态申请锁数量) * sizeof(NamedLWLockTranche) | NamedLWLockTranche{int trancheId, char *trancheName}
| | 注意:trancheName字符串只有指针,空间下面申请。
|--------------------------------------------------|
| |
| (遍历数组拿到所有动态申请锁的名字的总字符数) | 申请空间,给上面使用。
| |
|--------------------------------------------------|
初始化位置
main
PostmasterMain
reset_shared
CreateSharedMemoryAndSemaphores
CreateLWLocks
InitializeLWLocks // 初始化所有锁,包括fixed lwlock 和 动态注册的named lwlock
LWLockRegisterTranche // 注册named lwlock到统一命名体系
初始化函数:InitializeLWLocks
初始化过程分为两步:初始化固定锁、初始化动态锁。
初始化时会给每个锁结构LWLock添加tranche_id信息,同时将锁状态置为release。
初始化最后会为4.1中的内存结构中第三、四部分添加上数据,用NamedLWLockTrancheArray指向。
为了方便使用,在用LWLockTrancheNames数组,记录tranchid到tranche_name的映射。
static void
InitializeLWLocks(void)
{
int numNamedLocks = NumLWLocksForNamedTranches();
int id;
int i;
int j;
LWLockPadded *lock;
/* Initialize all individual LWLocks in main array */
for (id = 0, lock = MainLWLockArray; id < NUM_INDIVIDUAL_LWLOCKS; id++, lock++)
LWLockInitialize(&lock->lock, id);
// 共享名字、ID
/* Initialize buffer mapping LWLocks in main array */
lock = MainLWLockArray + BUFFER_MAPPING_LWLOCK_OFFSET;
for (id = 0; id < NUM_BUFFER_PARTITIONS; id++, lock++)
LWLockInitialize(&lock->lock, LWTRANCHE_BUFFER_MAPPING);
// 共享名字、ID
/* Initialize lmgrs' LWLocks in main array */
lock = MainLWLockArray + LOCK_MANAGER_LWLOCK_OFFSET;
for (id = 0; id < NUM_LOCK_PARTITIONS; id++, lock++)
LWLockInitialize(&lock->lock, LWTRANCHE_LOCK_MANAGER);
/* Initialize predicate lmgrs' LWLocks in main array */
lock = MainLWLockArray + PREDICATELOCK_MANAGER_LWLOCK_OFFSET;
for (id = 0; id < NUM_PREDICATELOCK_PARTITIONS; id++, lock++)
LWLockInitialize(&lock->lock, LWTRANCHE_PREDICATE_LOCK_MANAGER);
/*
* Copy the info about any named tranches into shared memory (so that
* other processes can see it), and initialize the requested LWLocks.
*/
if (NamedLWLockTrancheRequests > 0)
{
char *trancheNames;
NamedLWLockTrancheArray = (NamedLWLockTranche *)
&MainLWLockArray[NUM_FIXED_LWLOCKS + numNamedLocks];
trancheNames = (char *) NamedLWLockTrancheArray +
(NamedLWLockTrancheRequests * sizeof(NamedLWLockTranche));
lock = &MainLWLockArray[NUM_FIXED_LWLOCKS];
for (i = 0; i < NamedLWLockTrancheRequests; i++)
{
NamedLWLockTrancheRequest *request;
NamedLWLockTranche *tranche;
char *name;
request = &NamedLWLockTrancheRequestArray[i];
tranche = &NamedLWLockTrancheArray[i];
name = trancheNames;
trancheNames += strlen(request->tranche_name) + 1;
strcpy(name, request->tranche_name);
tranche->trancheId = LWLockNewTrancheId();
tranche->trancheName = name;
for (j = 0; j < request->num_lwlocks; j++, lock++)
LWLockInitialize(&lock->lock, tranche->trancheId);
}
}
}
/* 初始化时会给每个锁结构添加tranche_id */
void
LWLockInitialize(LWLock *lock, int tranche_id)
{
pg_atomic_init_u32(&lock->state, LW_FLAG_RELEASE_OK);
lock->tranche = tranche_id;
proclist_init(&lock->waiters);
}
固定锁共72个名字,保存在两个数组中:IndividualLWLockNames、BuiltinTrancheNames
前48个名字在IndividualLWLockNames中。
后24个名字在BuiltinTrancheNames中。
使用72以内的ID查询,会使用两个数组直接返回字符串。
使用72以上的ID查询,返回LWLockTrancheNames对应的动态锁名,查不到就返回extension。
static const char *
GetLWTrancheName(uint16 trancheId)
{
/* Individual LWLock? */
if (trancheId < NUM_INDIVIDUAL_LWLOCKS)
return IndividualLWLockNames[trancheId];
/* Built-in tranche? */
if (trancheId < LWTRANCHE_FIRST_USER_DEFINED)
return BuiltinTrancheNames[trancheId - NUM_INDIVIDUAL_LWLOCKS];
/*
* It's an extension tranche, so look in LWLockTrancheNames[]. However,
* it's possible that the tranche has never been registered in the current
* process, in which case give up and return "extension".
*/
trancheId -= LWTRANCHE_FIRST_USER_DEFINED;
if (trancheId >= LWLockTrancheNamesAllocated ||
LWLockTrancheNames[trancheId] == NULL)
return "extension";
return LWLockTrancheNames[trancheId];
}