/* * PGReserveSemaphores --- initialize semaphore support * * This is called during postmaster start or shared memory reinitialization. * It should do whatever is needed to be able to support up to maxSemas * subsequent PGSemaphoreCreate calls. Also, if any system resources * are acquired here or in PGSemaphoreCreate, register an on_shmem_exit * callback to release them. * * The port number is passed for possible use as a key (for Posix, we use * it to generate the starting semaphore name). In a standalone backend, * zero will be passed. * * In the Posix implementation, we acquire semaphores on-demand; the * maxSemas parameter is just used to size the arrays. For unnamed * semaphores, there is an array of PGSemaphoreData structs in shared memory. * For named semaphores, we keep a postmaster-local array of sem_t pointers, * which we use for releasing the semphores when done. * (This design minimizes the dependency of postmaster shutdown on the * contents of shared memory, which a failed backend might have clobbered. * We can't do much about the possibility of sem_destroy() crashing, but * we don't have to expose the counters to other processes.) */ void PGReserveSemaphores(int maxSemas, int port) { #ifdef USE_NAMED_POSIX_SEMAPHORES mySemPointers = (sem_t **) malloc(maxSemas * sizeof(sem_t *)); if (mySemPointers == NULL) elog(PANIC, "out of memory"); #else /* * We must use ShmemAllocUnlocked(), since the spinlock protecting * ShmemAlloc() won't be ready yet. (This ordering is necessary when we * are emulating spinlocks with semaphores.) */ sharedSemas = (PGSemaphore) ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas)); #endif numSems = 0; maxSems = maxSemas; nextSemKey = port * 1000; on_shmem_exit(ReleaseSemaphores, 0); }
/* * PGReserveSemaphores --- initialize semaphore support * * This is called during postmaster start or shared memory reinitialization. * It should do whatever is needed to be able to support up to maxSemas * subsequent PGSemaphoreCreate calls. Also, if any system resources * are acquired here or in PGSemaphoreCreate, register an on_shmem_exit * callback to release them. * * The port number is passed for possible use as a key (for SysV, we use * it to generate the starting semaphore key). In a standalone backend, * zero will be passed. * * In the SysV implementation, we acquire semaphore sets on-demand; the * maxSemas parameter is just used to size the arrays. There is an array * of PGSemaphoreData structs in shared memory, and a postmaster-local array * with one entry per SysV semaphore set, which we use for releasing the * semaphore sets when done. (This design ensures that postmaster shutdown * doesn't rely on the contents of shared memory, which a failed backend might * have clobbered.) */ void PGReserveSemaphores(int maxSemas, int port) { /* * We must use ShmemAllocUnlocked(), since the spinlock protecting * ShmemAlloc() won't be ready yet. (This ordering is necessary when we * are emulating spinlocks with semaphores.) */ sharedSemas = (PGSemaphore) ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas)); numSharedSemas = 0; maxSharedSemas = maxSemas; maxSemaSets = (maxSemas + SEMAS_PER_SET - 1) / SEMAS_PER_SET; mySemaSets = (IpcSemaphoreId *) malloc(maxSemaSets * sizeof(IpcSemaphoreId)); if (mySemaSets == NULL) elog(PANIC, "out of memory"); numSemaSets = 0; nextSemaKey = port * 1000; nextSemaNumber = SEMAS_PER_SET; /* force sema set alloc on 1st call */ on_shmem_exit(ReleaseSemaphores, 0); }
/* * CreateSharedMemoryAndSemaphores * Creates and initializes shared memory and semaphores. * * This is called by the postmaster or by a standalone backend. * It is also called by a backend forked from the postmaster in the * EXEC_BACKEND case. In the latter case, the shared memory segment * already exists and has been physically attached to, but we have to * initialize pointers in local memory that reference the shared structures, * because we didn't inherit the correct pointer values from the postmaster * as we do in the fork() scenario. The easiest way to do that is to run * through the same code as before. (Note that the called routines mostly * check IsUnderPostmaster, rather than EXEC_BACKEND, to detect this case. * This is a bit code-wasteful and could be cleaned up.) * * If "makePrivate" is true then we only need private memory, not shared * memory. This is true for a standalone backend, false for a postmaster. */ void CreateSharedMemoryAndSemaphores(bool makePrivate, int port) { PGShmemHeader *shim = NULL; if (!IsUnderPostmaster) { PGShmemHeader *seghdr; Size size; int numSemas; /* Compute number of semaphores we'll need */ numSemas = ProcGlobalSemas(); numSemas += SpinlockSemas(); /* * Size of the Postgres shared-memory block is estimated via * moderately-accurate estimates for the big hogs, plus 100K for the * stuff that's too small to bother with estimating. * * We take some care during this phase to ensure that the total size * request doesn't overflow size_t. If this gets through, we don't * need to be so careful during the actual allocation phase. */ size = 100000; size = add_size(size, PGSemaphoreShmemSize(numSemas)); size = add_size(size, SpinlockSemaSize()); size = add_size(size, hash_estimate_size(SHMEM_INDEX_SIZE, sizeof(ShmemIndexEnt))); size = add_size(size, BufferShmemSize()); size = add_size(size, LockShmemSize()); size = add_size(size, PredicateLockShmemSize()); size = add_size(size, ProcGlobalShmemSize()); size = add_size(size, XLOGShmemSize()); size = add_size(size, CLOGShmemSize()); size = add_size(size, CommitTsShmemSize()); size = add_size(size, SUBTRANSShmemSize()); size = add_size(size, TwoPhaseShmemSize()); size = add_size(size, BackgroundWorkerShmemSize()); size = add_size(size, MultiXactShmemSize()); size = add_size(size, LWLockShmemSize()); size = add_size(size, ProcArrayShmemSize()); size = add_size(size, BackendStatusShmemSize()); size = add_size(size, SInvalShmemSize()); size = add_size(size, PMSignalShmemSize()); size = add_size(size, ProcSignalShmemSize()); size = add_size(size, CheckpointerShmemSize()); size = add_size(size, AutoVacuumShmemSize()); size = add_size(size, ReplicationSlotsShmemSize()); size = add_size(size, ReplicationOriginShmemSize()); size = add_size(size, WalSndShmemSize()); size = add_size(size, WalRcvShmemSize()); size = add_size(size, ApplyLauncherShmemSize()); size = add_size(size, SnapMgrShmemSize()); size = add_size(size, BTreeShmemSize()); size = add_size(size, SyncScanShmemSize()); size = add_size(size, AsyncShmemSize()); #ifdef EXEC_BACKEND size = add_size(size, ShmemBackendArraySize()); #endif /* freeze the addin request size and include it */ addin_request_allowed = false; size = add_size(size, total_addin_request); /* might as well round it off to a multiple of a typical page size */ size = add_size(size, 8192 - (size % 8192)); elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size); /* * Create the shmem segment */ seghdr = PGSharedMemoryCreate(size, makePrivate, port, &shim); InitShmemAccess(seghdr); /* * Create semaphores */ PGReserveSemaphores(numSemas, port); /* * If spinlocks are disabled, initialize emulation layer (which * depends on semaphores, so the order is important here). */ #ifndef HAVE_SPINLOCKS SpinlockSemaInit(); #endif } else { /* * We are reattaching to an existing shared memory segment. This * should only be reached in the EXEC_BACKEND case, and even then only * with makePrivate == false. */ #ifdef EXEC_BACKEND Assert(!makePrivate); #else elog(PANIC, "should be attached to shared memory already"); #endif } /* * Set up shared memory allocation mechanism */ if (!IsUnderPostmaster) InitShmemAllocation(); /* * Now initialize LWLocks, which do shared memory allocation and are * needed for InitShmemIndex. */ CreateLWLocks(); /* * Set up shmem.c index hashtable */ InitShmemIndex(); /* * Set up xlog, clog, and buffers */ XLOGShmemInit(); CLOGShmemInit(); CommitTsShmemInit(); SUBTRANSShmemInit(); MultiXactShmemInit(); InitBufferPool(); /* * Set up lock manager */ InitLocks(); /* * Set up predicate lock manager */ InitPredicateLocks(); /* * Set up process table */ if (!IsUnderPostmaster) InitProcGlobal(); CreateSharedProcArray(); CreateSharedBackendStatus(); TwoPhaseShmemInit(); BackgroundWorkerShmemInit(); /* * Set up shared-inval messaging */ CreateSharedInvalidationState(); /* * Set up interprocess signaling mechanisms */ PMSignalShmemInit(); ProcSignalShmemInit(); CheckpointerShmemInit(); AutoVacuumShmemInit(); ReplicationSlotsShmemInit(); ReplicationOriginShmemInit(); WalSndShmemInit(); WalRcvShmemInit(); ApplyLauncherShmemInit(); /* * Set up other modules that need some shared memory space */ SnapMgrInit(); BTreeShmemInit(); SyncScanShmemInit(); AsyncShmemInit(); #ifdef EXEC_BACKEND /* * Alloc the win32 shared backend array */ if (!IsUnderPostmaster) ShmemBackendArrayAllocation(); #endif /* Initialize dynamic shared memory facilities. */ if (!IsUnderPostmaster) dsm_postmaster_startup(shim); /* * Now give loadable modules a chance to set up their shmem allocations */ if (shmem_startup_hook) shmem_startup_hook(); }