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 先上个图,看一下函数调用过程梗概,中间略过部分细节

 


初始化缓冲池 /buffer pool 方法调用流程图

 

2 初始化 xlog 相关结构

话说 main()->…->PostmasterMain()->…->reset_shared() -> CreateSharedMemoryAndSemaphores()->…-> InitBufferPool () ,初始化 缓冲池及 相关数据结构 BufferDesc 等,然后又初始化了一个可扩展哈希表 "shared buffer lookup 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 buffer lookup table" (图在下面)。返回 InitBufferPool () ,让 HTAB * 类型 全局静态变量 SharedBufHash 指向 "shared buffer lookup table"

最后 StrategyInitialize() -> ShmemInitStruct() 在其中 调用 hash_search() 在哈希表索引 "ShmemIndex" 中查找 "Buffer Strategy Status" ,如果没有,就在 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 结构类型的成员值。

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

 

typedef struct sbufdesc

{

       BufferTag      tag;                     /* ID of page contained in buffer */

       BufFlags       flags;                   /* see bit definitions above */

       uint16           usage_count;     /* usage counter for clock sweep code */

       unsigned     refcount;             /* # of backends holding pins on buffer */

       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 shared freelist control information.

  */

typedef struct

{

    /* Clock sweep hand: index of next buffer to consider grabbing */

    int          nextVictimBuffer;

 

    int          firstFreeBuffer;    /* Head of list of unused buffers */

    int          lastFreeBuffer; /* Tail of list of unused buffers */

 

    /*

      * NOTE: lastFreeBuffer is undefined when firstFreeBuffer is -1 (that is,

      * when the list is empty)

      */

 

    /*

      * Statistics.  These counters should be wide enough that they can't

      * overflow during a single bgwriter cycle.

      */

    uint32      completePasses;     /* Complete cycles of the clock sweep */

    uint32      numBufferAllocs;    /* Buffers allocated since last reset */    

} BufferStrategyControl;

 

/* Pointers to shared state */

static BufferStrategyControl *StrategyControl = NULL;

 

static HTAB *SharedBufHash;

 

typedef struct buftag

{

    RelFileNode rnode;          /* physical relation identifier */

    ForkNumber forkNum;

    BlockNumber blockNum;       /* blknum relative to begin of reln */

} BufferTag;

 

typedef struct

{

    BufferTag   key;            /* Tag of a disk page */

    int          id;             /* Associated buffer ID */

} BufferLookupEnt;

 


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

 

 

 

BufferPool 哈希表索引“ Shared Buffer Lookup Table

 

 

 

BufferPool 相关结构和策略控制结构图

       上图中左面是 BufferPool 和策略控制结构图,根据 shared_buffer 的设置,有 25600 BufferDesc 数组和 25600 buffer block 数组,还有一个 BufferStrategyControl 结构。右面是 25600 BufferDesc 数组中每个 BufferDesc 的成员的初始化值。已经体现了一个 buffer block 两个轻量锁以及和磁盘上的数据文件的对应情况。