PostgreSQL启动过程中的那些事七:初始化共享内存和信号七:shmem中初始化bufferpool
pg初始化完shmem,给其加上索引"ShmemIndex"后,接着就在shmem里初始化管理各种事务和事务本身相关结构的实例。然后就是初始化缓冲池(buffer pool)。
缓冲区(buffers)存在于一个空闲内存块列表和一个哈希表查询数据结构。下面简述一下和缓冲池相关的概念。
查找缓冲区(buffer)时必须注意,在I/O开始之前缓冲区必须可用。负责尝试读缓冲区的第二个进程会分配自己的复制,这样缓冲池就不一致了。
缓冲区同步,IO_IN_PROGRESS——这是缓冲区描述符里的一个标签。当一个IO被启动和在IO结束被清除时必须设置该标签。这样是为了保证在另一个进程使用该缓冲区的时候其他进程不能开始使用该缓冲区。
缓冲区缓冲块的引用计数——计进程在缓冲区上持有pin的数目。缓冲区在IO期间,BufferAlloc()之后立即被pin住。事务结束之前pin被释放。
私有引用计数——每一个缓冲区有一个私有的引用计数,保持当前进程里pin住该缓冲区的次数跟踪。用这个有两个目的:第一,如果pin住一个缓冲区多于一次,pg仅需要改变共享的引用计数一次,这样只锁共享状态一次;第二,当事务退出时,它应该仅unpin缓冲区正好自己pin住该缓冲区的次数,这样该事务就可以不破坏了另一个后台进程/backend的缓冲区。
1先上个图,看一下函数调用过程梗概,中间略过部分细节
初始化缓冲池/bufferpool方法调用流程图
2初始化xlog相关结构
话说main()->…->PostmasterMain()->…->reset_shared()-> CreateSharedMemoryAndSemaphores()->…-> InitBufferPool(),初始化缓冲池及相关数据结构BufferDesc等,然后又初始化了一个可扩展哈希表"shared bufferlookup table",用作内存里管理缓冲池。
InitBufferPool()->ShmemInitStruct(),在其中调用hash_search()在哈希表索引"ShmemIndex"中查找"Buffer Descriptors",如果没有,就在shmemIndex中给"Buffer Descriptors"分一个HashElement和ShmemIndexEnt(entry),在其中的Entry中写上"Buffer Descriptors"。返回ShmemInitStruct(),再调用ShmemAlloc()在共享内存上给"Buffer Descriptors"相关结构(是该结构的数组,数组数目根据shared_buffer计算,有数万甚至数十万以上,具体见下面“BufferPool相关结构和策略控制结构图”)分配空间,设置entry(在这儿及ShmemIndexEnt类型变量)的成员location指向该空间,size成员记录该空间大小,最后返回InitBufferPool(),让BufferDesc *类型全局变量BufferDescriptors指向BufferDesc类型实例的起始地址就是在shmem里给"Buffer Descriptors"相关结构分配的内存起始地址,设置其中BufferDesc结构类型的成员值。
接着InitBufferPool()->ShmemInitStruct(),在其中调用hash_search()在哈希表索引"ShmemIndex"中查找"Buffer Blocks",如果没有,就在shmemIndex中给"Buffer Blocks"分一个HashElement和ShmemIndexEnt(entry),在其中的Entry中写上"Buffer Blocks"。返回ShmemInitStruct(),再调用ShmemAlloc()在共享内存上给"Buffer Blocks"分配空间,设置entry(在这儿及ShmemIndexEnt类型变量)的成员location指向该空间,size成员记录该空间大小。每个缓冲区块大小默认为8k(可以根据设置变),共shared_buffer/BLCKSZ个,一般会有数万个甚至数十万个以上,一个缓冲区块一个BufferDescriptors。最后返回InitBufferPool(),让char *类型全局变量BufferBlocks指向该内存段的起始地址。
然后StrategyInitialize()->InitBufferTable()->ShmemInitHash(),在其中创建一个用于管理和查找缓冲区块的可扩展哈希表"shared bufferlookup table"(图在下面)。返回InitBufferPool(),让HTAB *类型全局静态变量SharedBufHash指向"shared bufferlookup table"。
最后StrategyInitialize()->ShmemInitStruct(),在其中调用hash_search()在哈希表索引"ShmemIndex"中查找"Buffer StrategyStatus",如果没有,就在shmemIndex中给"Buffer Strategy Status"分一个HashElement和ShmemIndexEnt(entry),在其中的Entry中写上"Buffer Strategy Status"。返回ShmemInitStruct(),再调用ShmemAlloc()在共享内存上给"Buffer Strategy Status"相关结构(见下面“BufferPool相关结构和策略控制结构图”)分配空间,设置entry(在这儿及ShmemIndexEnt类型变量)的成员location指向该空间,size成员记录该空间大小,最后返回StrategyInitialize(),让BufferStrategyControl *类型全局静态变量StrategyControl指向BufferStrategyControl类型实例的起始地址就是在shmem里给"Buffer Strategy Status"相关结构分配的内存起始地址,设置其中BufferStrategyControl结构类型的成员值。
相关变量、结构定义和初始化完成后数据结构图在下面。
typedefstruct sbufdesc
{
BufferTag tag; /* ID of page contained inbuffer */
BufFlags flags; /* see bit definitions above*/
uint16 usage_count; /* usage counter for clock sweep code */
unsigned refcount; /* # of backends holding pins onbuffer */
int wait_backend_pid; /* backend PID of pin-count waiter */
slock_t buf_hdr_lock; /* protects the above fields */
int buf_id; /* buffer's index number (from 0) */
int freeNext; /* link in freelist chain */
LWLockId io_in_progress_lock; /* to wait for I/O to complete */
LWLockId content_lock; /* to lock access to buffer contents */
}BufferDesc;
/*
* The sharedfreelist control information.
*/
typedef struct
{
/* Clock sweep hand: index of next buffer to considergrabbing */
int nextVictimBuffer;
int firstFreeBuffer; /* Head of list ofunused buffers */
int lastFreeBuffer;/* Tail of list of unused buffers */