📄 shmem.c
字号:
/*------------------------------------------------------------------------- * * shmem.c * create shared memory and initialize shared memory data structures. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $Header: /usr/local/cvsroot/pgsql/src/backend/storage/ipc/shmem.c,v 1.42 1999/06/06 20:19:35 vadim Exp $ * *------------------------------------------------------------------------- *//* * POSTGRES processes share one or more regions of shared memory. * The shared memory is created by a postmaster and is inherited * by each backends via fork(). The routines in this file are used for * allocating and binding to shared memory data structures. * * NOTES: * (a) There are three kinds of shared memory data structures * available to POSTGRES: fixed-size structures, queues and hash * tables. Fixed-size structures contain things like global variables * for a module and should never be allocated after the process * initialization phase. Hash tables have a fixed maximum size, but * their actual size can vary dynamically. When entries are added * to the table, more space is allocated. Queues link data structures * that have been allocated either as fixed size structures or as hash * buckets. Each shared data structure has a string name to identify * it (assigned in the module that declares it). * * (b) During initialization, each module looks for its * shared data structures in a hash table called the "Shmem Index". * If the data structure is not present, the caller can allocate * a new one and initialize it. If the data structure is present, * the caller "attaches" to the structure by initializing a pointer * in the local address space. * The shmem index has two purposes: first, it gives us * a simple model of how the world looks when a backend process * initializes. If something is present in the shmem index, * it is initialized. If it is not, it is uninitialized. Second, * the shmem index allows us to allocate shared memory on demand * instead of trying to preallocate structures and hard-wire the * sizes and locations in header files. If you are using a lot * of shared memory in a lot of different places (and changing * things during development), this is important. * * (c) memory allocation model: shared memory can never be * freed, once allocated. Each hash table has its own free list, * so hash buckets can be reused when an item is deleted. However, * if one hash table grows very large and then shrinks, its space * cannot be redistributed to other tables. We could build a simple * hash bucket garbage collector if need be. Right now, it seems * unnecessary. * * See InitSem() in sem.c for an example of how to use the * shmem index. * */#include <stdio.h>#include <string.h>#include "postgres.h"#include "storage/ipc.h"#include "storage/shmem.h"#include "storage/spin.h"#include "storage/proc.h"#include "utils/hsearch.h"#include "utils/memutils.h"#include "access/xact.h"#include "utils/tqual.h"/* shared memory global variables */unsigned long ShmemBase = 0; /* start and end address of shared memory */static unsigned long ShmemEnd = 0;static unsigned long ShmemSize = 0; /* current size (and default) */extern VariableCache ShmemVariableCache; /* varsup.c */SPINLOCK ShmemLock; /* lock for shared memory allocation */SPINLOCK ShmemIndexLock; /* lock for shmem index access */static unsigned long *ShmemFreeStart = NULL; /* pointer to the OFFSET * of first free shared * memory */static unsigned long *ShmemIndexOffset = NULL; /* start of the shmem * index table (for * bootstrap) */static int ShmemBootstrap = FALSE; /* flag becomes true when shared * mem is created by POSTMASTER */static HTAB *ShmemIndex = NULL;/* --------------------- * ShmemIndexReset() - Resets the shmem index to NULL.... * useful when the postmaster destroys existing shared memory * and creates all new segments after a backend crash. * ---------------------- */voidShmemIndexReset(void){ ShmemIndex = (HTAB *) NULL;}/* * CreateSharedRegion() * * This routine is called once by the postmaster to * initialize the shared buffer pool. Assume there is * only one postmaster so no synchronization is necessary * until after this routine completes successfully. * * key is a unique identifier for the shmem region. * size is the size of the region. */static IpcMemoryId ShmemId;voidShmemCreate(unsigned int key, unsigned int size){ if (size) ShmemSize = size; /* create shared mem region */ if ((ShmemId = IpcMemoryCreate(key, ShmemSize, IPCProtection)) == IpcMemCreationFailed) { elog(FATAL, "ShmemCreate: cannot create region"); exit(1); } /* * ShmemBootstrap is true if shared memory has been created, but not * yet initialized. Only the postmaster/creator-of-all-things should * have this flag set. */ ShmemBootstrap = TRUE;}/* * InitShmem() -- map region into process address space * and initialize shared data structures. * */intInitShmem(unsigned int key, unsigned int size){ Pointer sharedRegion; unsigned long currFreeSpace; HASHCTL info; int hash_flags; ShmemIndexEnt *result, item; bool found; IpcMemoryId shmid; /* if zero key, use default memory size */ if (size) ShmemSize = size; /* default key is 0 */ /* attach to shared memory region (SysV or BSD OS specific) */ if (ShmemBootstrap && key == PrivateIPCKey) /* if we are running backend alone */ shmid = ShmemId; else shmid = IpcMemoryIdGet(IPCKeyGetBufferMemoryKey(key), ShmemSize); sharedRegion = IpcMemoryAttach(shmid); if (sharedRegion == NULL) { elog(FATAL, "AttachSharedRegion: couldn't attach to shmem\n"); return FALSE; } /* get pointers to the dimensions of shared memory */ ShmemBase = (unsigned long) sharedRegion; ShmemEnd = (unsigned long) sharedRegion + ShmemSize; currFreeSpace = 0; /* First long in shared memory is the count of available space */ ShmemFreeStart = (unsigned long *) ShmemBase; /* next is a shmem pointer to the shmem index */ ShmemIndexOffset = ShmemFreeStart + 1; /* next is ShmemVariableCache */ ShmemVariableCache = (VariableCache) (ShmemIndexOffset + 1); currFreeSpace += sizeof(ShmemFreeStart) + sizeof(ShmemIndexOffset) + LONGALIGN(sizeof(VariableCacheData)); /* * bootstrap initialize spin locks so we can start to use the * allocator and shmem index. */ if (!InitSpinLocks(ShmemBootstrap, IPCKeyGetSpinLockSemaphoreKey(key))) return FALSE; /* * We have just allocated additional space for two spinlocks. Now * setup the global free space count */ if (ShmemBootstrap) { *ShmemFreeStart = currFreeSpace; memset(ShmemVariableCache, 0, sizeof(*ShmemVariableCache)); } /* if ShmemFreeStart is NULL, then the allocator won't work */ Assert(*ShmemFreeStart); /* create OR attach to the shared memory shmem index */ info.keysize = SHMEM_INDEX_KEYSIZE; info.datasize = SHMEM_INDEX_DATASIZE; hash_flags = HASH_ELEM; /* This will acquire the shmem index lock, but not release it. */ ShmemIndex = ShmemInitHash("ShmemIndex", SHMEM_INDEX_SIZE, SHMEM_INDEX_SIZE, &info, hash_flags); if (!ShmemIndex) { elog(FATAL, "InitShmem: couldn't initialize Shmem Index"); return FALSE; } /* * Now, check the shmem index for an entry to the shmem index. If * there is an entry there, someone else created the table. Otherwise, * we did and we have to initialize it. */ MemSet(item.key, 0, SHMEM_INDEX_KEYSIZE); strncpy(item.key, "ShmemIndex", SHMEM_INDEX_KEYSIZE); result = (ShmemIndexEnt *) hash_search(ShmemIndex, (char *) &item, HASH_ENTER, &found); if (!result) { elog(FATAL, "InitShmem: corrupted shmem index"); return FALSE; } if (!found) { /* * bootstrapping shmem: we have to initialize the shmem index now. */ Assert(ShmemBootstrap); result->location = MAKE_OFFSET(ShmemIndex->hctl); *ShmemIndexOffset = result->location; result->size = SHMEM_INDEX_SIZE; ShmemBootstrap = FALSE; } else Assert(!ShmemBootstrap); /* now release the lock acquired in ShmemHashInit */ SpinRelease(ShmemIndexLock); Assert(result->location == MAKE_OFFSET(ShmemIndex->hctl)); return TRUE;}/* * ShmemAlloc -- allocate word-aligned byte string from * shared memory * * Assumes ShmemLock and ShmemFreeStart are initialized. * Returns: real pointer to memory or NULL if we are out * of space. Has to return a real pointer in order * to be compatable with malloc(). */long *ShmemAlloc(unsigned long size){ unsigned long tmpFree; long *newSpace; /* * ensure space is word aligned. * * Word-alignment is not good enough. We have to be more conservative: * doubles need 8-byte alignment. (We probably only need this on RISC * platforms but this is not a big waste of space.) - ay 12/94 */ if (size % sizeof(double)) size += sizeof(double) - (size % sizeof(double)); Assert(*ShmemFreeStart); SpinAcquire(ShmemLock); tmpFree = *ShmemFreeStart + size; if (tmpFree <= ShmemSize) { newSpace = (long *) MAKE_PTR(*ShmemFreeStart); *ShmemFreeStart += size; } else newSpace = NULL; SpinRelease(ShmemLock); if (!newSpace) elog(NOTICE, "ShmemAlloc: out of memory "); return newSpace;}/* * ShmemIsValid -- test if an offset refers to valid shared memory * * Returns TRUE if the pointer is valid. */intShmemIsValid(unsigned long addr){ return (addr < ShmemEnd) && (addr >= ShmemBase);}/* * ShmemInitHash -- Create/Attach to and initialize * shared memory hash table. * * Notes: * * assume caller is doing some kind of synchronization * so that two people dont try to create/initialize the * table at once. Use SpinAlloc() to create a spinlock * for the structure before creating the structure itself. */HTAB *ShmemInitHash(char *name, /* table string name for shmem index */ long init_size, /* initial table size */ long max_size, /* max size of the table (NOT USED) */ HASHCTL *infoP, /* info about key and bucket size */ int hash_flags) /* info about infoP */{ bool found; long *location; /* * Hash tables allocated in shared memory have a fixed directory; it * can't grow or other backends wouldn't be able to find it. The * segbase is for calculating pointer values. The shared memory * allocator must be specified too. */ infoP->dsize = infoP->max_dsize = DEF_DIRSIZE; infoP->segbase = (long *) ShmemBase; infoP->alloc = ShmemAlloc; hash_flags |= HASH_SHARED_MEM | HASH_DIRSIZE; /* look it up in the shmem index */ location = ShmemInitStruct(name, sizeof(HHDR) + DEF_DIRSIZE * sizeof(SEG_OFFSET), &found); /* * shmem index is corrupted. Let someone else give the error * message since they have more information */ if (location == NULL) return 0; /* * it already exists, attach to it rather than allocate and initialize * new space */ if (found) hash_flags |= HASH_ATTACH; /* Now provide the header and directory pointers */ infoP->hctl = (long *) location;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -