postgresql内核源码分析-lwlock初始化_senllang的博客-爱代码爱编程
文章目录
- 前言
- 代码流程
- 结构说明
- 初始化过程
- 代码附录
- 结尾
前言
本文是基于postgresql 15的代码进行分析解读,演示是在centos8系统上进行。
代码流程
BaseInit
InitCommunication
CreateSharedMemoryAndSemaphores
CreateLWLocks
调用CreateLWLocks创建共享内存空间,并且初始化命名锁和tranche id等;因为是共享内存,所以只在master进程中初始化一次;BaseInit是在每个进程的主函数中调用。
结构说明
-
锁的结构定义
/*
* Code outside of lwlock.c should not manipulate the contents of this
* structure directly, but we have to declare it here to allow LWLocks to be
* incorporated into other data structures.
*/
typedef struct LWLock
{
uint16 tranche; /* tranche ID */
ux_atomic_uint32 state; /* state of exclusive/nonexclusive lockers */
proclist_head waiters; /* list of waiting UXPROCs */
#ifdef LOCK_DEBUG
ux_atomic_uint32 nwaiters; /* number of waiters */
struct UXPROC *owner; /* last exclusive owner of the lock */
#endif
} LWLock;
/* LWLock, padded to a full cache line size */
typedef union LWLockPadded
{
LWLock lock;
char pad[LWLOCK_PADDED_SIZE];
} LWLockPadded;
/* LWLock, minimally padded */
typedef union LWLockMinimallyPadded
{
LWLock lock;
char pad[LWLOCK_MINIMAL_SIZE];
} LWLockMinimallyPadded;
定义了两种 padded类型, full cache / minimal ,主要是针对扩展字节不同,full默认是128, minimal 可能是32/64;
extern UXDLLIMPORT LWLockPadded *MainLWLockArray;
extern char *MainLWLockNames[];
MainLWLockNames是整个lwlock的内存,信息记录总数组;
-
每个进程记录了自己持有的锁信息
/*
* We use this structure to keep track of locked LWLocks for release
* during error recovery. Normally, only a few will be held at once, but
* occasionally the number can be much higher; for example, the ux_buffercache
* extension locks all buffer partitions simultaneously.
*/
#define MAX_SIMUL_LWLOCKS 200
/* struct representing the LWLocks we're holding */
typedef struct LWLockHandle
{
LWLock *lock;
LWLockMode mode;
} LWLockHandle;
static int num_held_lwlocks = 0;
static LWLockHandle held_lwlocks[MAX_SIMUL_LWLOCKS];
每个进程最多可以持有 MAX_SIMUL_LWLOCKS 这个多个锁;特别像extension会持有很多锁;
-
通过extension注册的锁
/* struct representing the LWLock tranche request for named tranche */
typedef struct NamedLWLockTrancheRequest
{
char tranche_name[NAMEDATALEN];
int num_lwlocks;
} NamedLWLockTrancheRequest;
NamedLWLockTrancheRequest *NamedLWLockTrancheRequestArray = NULL;
int NamedLWLockTrancheRequests = 0;
static int NamedLWLockTrancheRequestsAllocated = 0;
在上面数组中注册lwlock name, 数量;
/* struct for storing named tranche information */
typedef struct NamedLWLockTranche
{
int trancheId;
char *trancheName;
} NamedLWLockTranche;
NamedLWLockTranche *NamedLWLockTrancheArray = NULL;
初始化过程
CreateLWLocks的步骤:
前三步只有在master中初始化,第四步每个进程都会初始化;
(1)MainLWLockArray 申请的共享内存的指针;前面留了一个整型空余;
LWLockShmemSize 获取lwlock占用的空间大小
(2) MainLWLockArray前的整型
见附件一定义代码;
2.1 赋初值为 LWTRANCHE_FIRST_USER_DEFINED是编译时确定的锁的数量,包括了保留的锁数量;
2.2 在LWLockNewTrancheId()调用中,对于NamedLWLockTrancheArray增加的锁,它们的trancheid是这个计数进行递增后的值
所以这个计数是所有lwlock的总数
(3)InitializeLWLocks 初始化锁的信息
3.1 保留锁信息初始化
也就是ID < NUM_INDIVIDUAL_LWLOCKS的锁;
初始化四类信息:
锁释放标志,置为LW_FLAG_RELEASE_OK
如果是调试状态,锁的等待数量置为0
tranche id 置为数据下标
锁等待者列表置为空
3.2 buffer mapping lwlock 初始化
也就是 ID < NUM_BUFFER_PARTITIONS
也是四类信息的初始化,但是tranche id 都为 LWTRANCHE_BUFFER_MAPPING
3.3 lock manage lwlock 初始化
也就是 ID < NUM_LOCK_PARTITIONS
也是四类信息的初始化,但是tranche id 都为 LWTRANCHE_LOCK_MANAGER
3.4 predicate lock manage lwlock 初始化
也就是 ID < NUM_PREDICATELOCK_PARTITIONS
也是四类信息的初始化,但是tranche id 都为 LWTRANCHE_PREDICATE_LOCK_MANAGER
3.5 如果 NamedLWLockTrancheRequests > 0;也就是有extension中注册了锁
这部分锁初始化由三部分构成:
一是名字name初始化;
二是trancheArray的初始化
三是lock的四类信息的初始化
此时,MainLWLockArray 在上面 保留锁和编译时生成的锁之后,按上面三部分 分成了三段,
第一段是lock,锁的数量由 NumLWLocksForNamedTranches()来统计;
第二段是tranche Array,每个成员是NamedLWLockTranche结构;
tranche id是由LWLockNewTrancheId()来获取;
NamedLWLockTranche *NamedLWLockTrancheArray 这个全局变量这指向这一段的起始位置
第三段是lock name,这里按char带结束符的字符串一个接一个存放;第二段中的trancheName来引用;
这部分的数据来源NamedLWLockTrancheRequestArray,extension需要在这个数组中先注册,数组元素个数由NamedLWLockTrancheRequests记录;
(4)注册extension的锁到各个进程中
static const char **LWLockTrancheNames = NULL;
static int LWLockTrancheNamesAllocated = 0;
就是初始化上面这两个全局变量,每个进程都会初始化;
在contextmemory中分配内存给LWLockTrancheNames ,这是一个字符指针的数组,下标是trancheid,指向NamedLWLockTrancheArray 中的name,也就是共享内存中的name;LWLockTrancheNamesAllocated 是数据分配的大小;
附件一:
/*
* Every tranche ID less than NUM_INDIVIDUAL_LWLOCKS is reserved; also,
* we reserve additional tranche IDs for builtin tranches not included in
* the set of individual LWLocks. A call to LWLockNewTrancheId will never
* return a value less than LWTRANCHE_FIRST_USER_DEFINED.
*/
typedef enum BuiltinTrancheIds
{
LWTRANCHE_XACT_BUFFER = NUM_INDIVIDUAL_LWLOCKS,
LWTRANCHE_COMMITTS_BUFFER,
LWTRANCHE_SUBTRANS_BUFFER,
LWTRANCHE_MULTIXACTOFFSET_BUFFER,
LWTRANCHE_MULTIXACTMEMBER_BUFFER,
LWTRANCHE_NOTIFY_BUFFER,
LWTRANCHE_SERIAL_BUFFER,
LWTRANCHE_WAL_INSERT,
LWTRANCHE_BUFFER_CONTENT,
LWTRANCHE_REPLICATION_ORIGIN_STATE,
LWTRANCHE_REPLICATION_SLOT_IO,
LWTRANCHE_LOCK_FASTPATH,
LWTRANCHE_BUFFER_MAPPING,
LWTRANCHE_LOCK_MANAGER,
LWTRANCHE_PREDICATE_LOCK_MANAGER,
LWTRANCHE_PARALLEL_HASH_JOIN,
LWTRANCHE_PARALLEL_QUERY_DSA,
LWTRANCHE_PER_SESSION_DSA,
LWTRANCHE_PER_SESSION_RECORD_TYPE,
LWTRANCHE_PER_SESSION_RECORD_TYPMOD,
LWTRANCHE_SHARED_TUPLESTORE,
LWTRANCHE_SHARED_TIDBITMAP,
LWTRANCHE_PARALLEL_APPEND,
LWTRANCHE_PER_XACT_PREDICATE_LIST,
LWTRANCHE_PGSTATS_DSA,
LWTRANCHE_PGSTATS_HASH,
LWTRANCHE_PGSTATS_DATA,
LWTRANCHE_FIRST_USER_DEFINED
} BuiltinTrancheIds;
结尾
作者邮箱:study@senllang.onaliyun.com
如有错误或者疏漏欢迎指出,互相学习。
注:未经同意,不得转载!