InitShmemIndex函数创建或attach共享内存索引表,HASHCTL是一个用于向hash_create函数输入标记位的结构体。InitShmemIndex函数会调用ShmemInitHash函数,该函数会创建、初始化或attach共享内存哈希表。共享内存哈希表在postmaster进程创建,子进程永远只会向存在的表中attach。max_size是哈希表条目的最大数量,init_size是预分配的哈希表条目的数量。 Note: before Postgres 9.0, this function returned NULL for some failure cases. Now, it always throws error instead, so callers need not check for NULL。

void InitShmemIndex(void) {
HASHCTL info;
int hash_flags;
info.keysize = SHMEM_INDEX_KEYSIZE; info.entrysize = sizeof(ShmemIndexEnt);
hash_flags = HASH_ELEM;
ShmemIndex = ShmemInitHash("ShmemIndex", SHMEM_INDEX_SIZE, SHMEM_INDEX_SIZE, &info, hash_flags);
}

HTAB *ShmemInitHash(const char *name, long init_size, long max_size, HASHCTL *infoP, int hash_flags) {
bool found;
void *location;
infoP->dsize = infoP->max_dsize = hash_select_dirsize(max_size); infoP->alloc = ShmemAllocNoError;
hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
/* look it up in the shmem index */
location = ShmemInitStruct(name, hash_get_shared_size(infoP, hash_flags), &found);
/* if it already exists, attach to it rather than allocate and initialize new space */
if (found)
hash_flags |= HASH_ATTACH;
/* Pass location of hashtable header to hash_create */
infoP->hctl = (HASHHDR *) location;
return hash_create(name, init_size, infoP, hash_flags);
}

PostgreSQL数据库共享内存——小管家InitShmemIndex函数_共享内存


hash_get_shared_size函数返回HASHHDR结构体的大小和所有的哈希段的大小。

Size hash_get_shared_size(HASHCTL *info, int flags) {
Assert(flags & HASH_DIRSIZE);
Assert(info->dsize == info->max_dsize);
return sizeof(HASHHDR) + info->dsize * sizeof(HASHSEGMENT);
}

ShmemInitStruct函数会返回上图右侧的HASHHDR的结构体,并将其attach到共享内存中。如果该结构体已经存在,foundPtr会设置为true。ShmemIndex是HTAB静态指针,如果为NULL,就说明还没有初始化共享内存索引表。先获取ShmemIndexLock,如果目前在Postmaster下,将shared memory index table的指针赋值给structPtr,将foundPtr会设置为true;如果不在Postmaster下,使用ShmemAlloc在共享内存给shared memory index table分配内存,并赋值给structPtr,将foundPtr会设置为false。最后释放锁,并返回shared memory index table的指针。

void *ShmemInitStruct(const char *name, Size size, bool *foundPtr) {
ShmemIndexEnt *result;
void *structPtr;
LWLockAcquire(ShmemIndexLock, LW_EXCLUSIVE);
if (!ShmemIndex){
PGShmemHeader *shmemseghdr = ShmemSegHdr;
/* Must be trying to create/attach to ShmemIndex itself */
Assert(strcmp(name, "ShmemIndex") == 0);
if (IsUnderPostmaster){
/* Must be initializing a (non-standalone) backend */
Assert(shmemseghdr->index != NULL);
structPtr = shmemseghdr->index;
*foundPtr = true;
}else{
Assert(shmemseghdr->index == NULL);
structPtr = ShmemAlloc(size);
shmemseghdr->index = structPtr;
*foundPtr = false;
}
LWLockRelease(ShmemIndexLock);
return structPtr;
}

/* look it up in the shmem index */
result = (ShmemIndexEnt *)hash_search(ShmemIndex, name, HASH_ENTER_NULL, foundPtr);
if (!result){
LWLockRelease(ShmemIndexLock);
ereport(ERROR,(errcode(ERRCODE_OUT_OF_MEMORY),errmsg("could not create ShmemIndex entry for data structure \"%s\"",name)));
}
if (*foundPtr){
/* Structure is in the shmem index so someone else has allocated it already. The size better be the same as the size we are trying to initialize to, or there is a name conflict (or worse). */
if (result->size != size){
LWLockRelease(ShmemIndexLock);
ereport(ERROR,(errmsg("ShmemIndex entry size is wrong for data structure \"%s\": expected %zu, actual %zu",name, size, result->size)));
}
structPtr = result->location;
}else{
/* It isn't in the table yet. allocate and initialize it */
structPtr = ShmemAllocNoError(size);
if (structPtr == NULL){
/* out of memory; remove the failed ShmemIndex entry */
hash_search(ShmemIndex, name, HASH_REMOVE, NULL);
LWLockRelease(ShmemIndexLock);
ereport(ERROR,(errcode(ERRCODE_OUT_OF_MEMORY),errmsg("not enough shared memory for data structure \"%s\" (%zu bytes requested)",name, size)));
}
result->size = size;
result->location = structPtr;
}
LWLockRelease(ShmemIndexLock);
Assert(ShmemAddrIsValid(structPtr));
Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
return structPtr;
}

PostgreSQL数据库共享内存——小管家InitShmemIndex函数_赋值_02