📄 proc.c
字号:
/*------------------------------------------------------------------------- * * proc.c * routines to manage per-process shared memory data structure * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $Header: /usr/local/cvsroot/pgsql/src/backend/storage/lmgr/proc.c,v 1.57 1999/05/25 22:42:03 momjian Exp $ * *------------------------------------------------------------------------- *//* * Each postgres backend gets one of these. We'll use it to * clean up after the process should the process suddenly die. * * * Interface (a): * ProcSleep(), ProcWakeup(), ProcWakeupNext(), * ProcQueueAlloc() -- create a shm queue for sleeping processes * ProcQueueInit() -- create a queue without allocing memory * * Locking and waiting for buffers can cause the backend to be * put to sleep. Whoever releases the lock, etc. wakes the * process up again (and gives it an error code so it knows * whether it was awoken on an error condition). * * Interface (b): * * ProcReleaseLocks -- frees the locks associated with this process, * ProcKill -- destroys the shared memory state (and locks) * associated with the process. * * 5/15/91 -- removed the buffer pool based lock chain in favor * of a shared memory lock chain. The write-protection is * more expensive if the lock chain is in the buffer pool. * The only reason I kept the lock chain in the buffer pool * in the first place was to allow the lock table to grow larger * than available shared memory and that isn't going to work * without a lot of unimplemented support anyway. * * 4/7/95 -- instead of allocating a set of 1 semaphore per process, we * allocate a semaphore from a set of PROC_NSEMS_PER_SET semaphores * shared among backends (we keep a few sets of semaphores around). * This is so that we can support more backends. (system-wide semaphore * sets run out pretty fast.) -ay 4/95 * * $Header: /usr/local/cvsroot/pgsql/src/backend/storage/lmgr/proc.c,v 1.57 1999/05/25 22:42:03 momjian Exp $ */#include <sys/time.h>#include <unistd.h>#include <string.h>#include <signal.h>#include <sys/types.h>#if defined(solaris_sparc)#include <sys/ipc.h>#include <sys/sem.h>#endif#include "postgres.h"#include "miscadmin.h"#include "libpq/pqsignal.h"#include "access/xact.h"#include "utils/hsearch.h"#include "storage/ipc.h"/* In Ultrix, sem.h must be included after ipc.h */#include <sys/sem.h>#include "storage/buf.h"#include "storage/lock.h"#include "storage/lmgr.h"#include "storage/shmem.h"#include "storage/spin.h"#include "storage/proc.h"#include "utils/trace.h"static void HandleDeadLock(int sig);static void ProcFreeAllSemaphores(void);#define DeadlockCheckTimer pg_options[OPT_DEADLOCKTIMEOUT]/* -------------------- * Spin lock for manipulating the shared process data structure: * ProcGlobal.... Adding an extra spin lock seemed like the smallest * hack to get around reading and updating this structure in shared * memory. -mer 17 July 1991 * -------------------- */SPINLOCK ProcStructLock;/* * For cleanup routines. Don't cleanup if the initialization * has not happened. */static bool ProcInitialized = FALSE;static PROC_HDR *ProcGlobal = NULL;PROC *MyProc = NULL;static void ProcKill(int exitStatus, int pid);static void ProcGetNewSemKeyAndNum(IPCKey *key, int *semNum);static void ProcFreeSem(IpcSemaphoreKey semKey, int semNum);static char *DeadLockMessage = "Deadlock detected -- See the lock(l) manual page for a possible cause.";/* * InitProcGlobal - * initializes the global process table. We put it here so that * the postmaster can do this initialization. (ProcFreeAllSemaphores needs * to read this table on exiting the postmaster. If we have the first * backend do this, starting up and killing the postmaster without * starting any backends will be a problem.) * * We also allocate all the per-process semaphores we will need to support * the requested number of backends. We used to allocate semaphores * only when backends were actually started up, but that is bad because * it lets Postgres fail under load --- a lot of Unix systems are * (mis)configured with small limits on the number of semaphores, and * running out when trying to start another backend is a common failure. * So, now we grab enough semaphores to support the desired max number * of backends immediately at initialization --- if the sysadmin has set * MaxBackends higher than his kernel will support, he'll find out sooner * rather than later. */voidInitProcGlobal(IPCKey key, int maxBackends){ bool found = false; /* attach to the free list */ ProcGlobal = (PROC_HDR *) ShmemInitStruct("Proc Header", (unsigned) sizeof(PROC_HDR), &found); /* -------------------- * We're the first - initialize. * XXX if found should ever be true, it is a sign of impending doom ... * ought to complain if so? * -------------------- */ if (!found) { int i; ProcGlobal->freeProcs = INVALID_OFFSET; ProcGlobal->currKey = IPCGetProcessSemaphoreInitKey(key); for (i = 0; i < MAX_PROC_SEMS / PROC_NSEMS_PER_SET; i++) ProcGlobal->freeSemMap[i] = 0; /* * Arrange to delete semas on exit --- set this up now so that we * will clean up if pre-allocation fails... */ on_shmem_exit(ProcFreeAllSemaphores, NULL); /* * Pre-create the semaphores for the first maxBackends processes, * unless we are running as a standalone backend. */ if (key != PrivateIPCKey) { for (i = 0; i < (maxBackends + PROC_NSEMS_PER_SET - 1) / PROC_NSEMS_PER_SET; i++) { IPCKey semKey = ProcGlobal->currKey + i; int semId; int semstat; semId = IpcSemaphoreCreate(semKey, PROC_NSEMS_PER_SET, IPCProtection, IpcSemaphoreDefaultStartValue, 0, &semstat); /* mark this sema set allocated */ ProcGlobal->freeSemMap[i] = (1 << PROC_NSEMS_PER_SET); } } }}/* ------------------------ * InitProc -- create a per-process data structure for this process * used by the lock manager on semaphore queues. * ------------------------ */voidInitProcess(IPCKey key){ bool found = false; int semstat; unsigned long location, myOffset; /* ------------------ * Routine called if deadlock timer goes off. See ProcSleep() * ------------------ */ pqsignal(SIGALRM, HandleDeadLock); SpinAcquire(ProcStructLock); /* attach to the free list */ ProcGlobal = (PROC_HDR *) ShmemInitStruct("Proc Header", (unsigned) sizeof(PROC_HDR), &found); if (!found) { /* this should not happen. InitProcGlobal() is called before this. */ elog(ERROR, "InitProcess: Proc Header uninitialized"); } if (MyProc != NULL) { SpinRelease(ProcStructLock); elog(ERROR, "ProcInit: you already exist"); return; } /* try to get a proc from the free list first */ myOffset = ProcGlobal->freeProcs; if (myOffset != INVALID_OFFSET) { MyProc = (PROC *) MAKE_PTR(myOffset); ProcGlobal->freeProcs = MyProc->links.next; } else { /* * have to allocate one. We can't use the normal shmem index * table mechanism because the proc structure is stored by PID * instead of by a global name (need to look it up by PID when we * cleanup dead processes). */ MyProc = (PROC *) ShmemAlloc((unsigned) sizeof(PROC)); if (!MyProc) { SpinRelease(ProcStructLock); elog(FATAL, "cannot create new proc: out of memory"); } /* this cannot be initialized until after the buffer pool */ SHMQueueInit(&(MyProc->lockQueue)); } /* * zero out the spin lock counts and set the sLocks field for * ProcStructLock to 1 as we have acquired this spinlock above but * didn't record it since we didn't have MyProc until now. */ MemSet(MyProc->sLocks, 0, sizeof(MyProc->sLocks)); MyProc->sLocks[ProcStructLock] = 1; if (IsUnderPostmaster) { IPCKey semKey; int semNum; int semId; union semun semun; ProcGetNewSemKeyAndNum(&semKey, &semNum); /* * Note: because of the pre-allocation done in InitProcGlobal, * this call should always attach to an existing semaphore. It * will (try to) create a new group of semaphores only if the * postmaster tries to start more backends than it said it would. */ semId = IpcSemaphoreCreate(semKey, PROC_NSEMS_PER_SET, IPCProtection, IpcSemaphoreDefaultStartValue, 0, &semstat); /* * we might be reusing a semaphore that belongs to a dead backend. * So be careful and reinitialize its value here. */ semun.val = IpcSemaphoreDefaultStartValue; semctl(semId, semNum, SETVAL, semun); IpcSemaphoreLock(semId, semNum, IpcExclusiveLock); MyProc->sem.semId = semId; MyProc->sem.semNum = semNum; MyProc->sem.semKey = semKey; } else MyProc->sem.semId = -1; /* ---------------------- * Release the lock. * ---------------------- */ SpinRelease(ProcStructLock); MyProc->pid = MyProcPid; MyProc->xid = InvalidTransactionId; MyProc->xmin = InvalidTransactionId; /* ---------------- * Start keeping spin lock stats from here on. Any botch before * this initialization is forever botched * ---------------- */ MemSet(MyProc->sLocks, 0, MAX_SPINS * sizeof(*MyProc->sLocks)); /* ------------------------- * Install ourselves in the shmem index table. The name to * use is determined by the OS-assigned process id. That * allows the cleanup process to find us after any untimely * exit. * ------------------------- */ location = MAKE_OFFSET(MyProc); if ((!ShmemPIDLookup(MyProcPid, &location)) || (location != MAKE_OFFSET(MyProc))) elog(FATAL, "InitProc: ShmemPID table broken"); MyProc->errType = NO_ERROR; SHMQueueElemInit(&(MyProc->links)); on_shmem_exit(ProcKill, (caddr_t) MyProcPid); ProcInitialized = TRUE;}/* * ProcReleaseLocks() -- release all locks associated with this process * */voidProcReleaseLocks(){ if (!MyProc) return; LockReleaseAll(1, &MyProc->lockQueue);}/* * ProcRemove - * used by the postmaster to clean up the global tables. This also frees * up the semaphore used for the lmgr of the process. (We have to do * this is the postmaster instead of doing a IpcSemaphoreKill on exiting * the process because the semaphore set is shared among backends and * we don't want to remove other's semaphores on exit.) */boolProcRemove(int pid){ SHMEM_OFFSET location; PROC *proc; location = INVALID_OFFSET; location = ShmemPIDDestroy(pid); if (location == INVALID_OFFSET) return FALSE; proc = (PROC *) MAKE_PTR(location); SpinAcquire(ProcStructLock); ProcFreeSem(proc->sem.semKey, proc->sem.semNum); proc->links.next = ProcGlobal->freeProcs; ProcGlobal->freeProcs = MAKE_OFFSET(proc); SpinRelease(ProcStructLock); return TRUE;}/* * ProcKill() -- Destroy the per-proc data structure for * this process. Release any of its held spin locks. */static voidProcKill(int exitStatus, int pid){ PROC *proc; SHMEM_OFFSET location; /* -------------------- * If this is a FATAL exit the postmaster will have to kill all the * existing backends and reinitialize shared memory. So all we don't * need to do anything here. * -------------------- */ if (exitStatus != 0) return; ShmemPIDLookup(MyProcPid, &location); if (location == INVALID_OFFSET) return; proc = (PROC *) MAKE_PTR(location); Assert(proc == MyProc || pid != MyProcPid); MyProc = NULL; /* --------------- * Assume one lock table. * --------------- */ ProcReleaseSpins(proc); LockReleaseAll(DEFAULT_LOCKMETHOD, &proc->lockQueue);#ifdef USER_LOCKS /* * Assume we have a second lock table. */ LockReleaseAll(USER_LOCKMETHOD, &proc->lockQueue);#endif /* ---------------- * get off the wait queue * ---------------- */ LockLockTable(); if (proc->links.next != INVALID_OFFSET) { Assert(proc->waitLock->waitProcs.size > 0); SHMQueueDelete(&(proc->links)); --proc->waitLock->waitProcs.size; } SHMQueueElemInit(&(proc->links)); UnlockLockTable(); return;}/* * ProcQueue package: routines for putting processes to sleep * and waking them up *//* * ProcQueueAlloc -- alloc/attach to a shared memory process queue * * Returns: a pointer to the queue or NULL * Side Effects: Initializes the queue if we allocated one */#ifdef NOT_USEDPROC_QUEUE *ProcQueueAlloc(char *name){ bool found; PROC_QUEUE *queue = (PROC_QUEUE *) ShmemInitStruct(name, (unsigned) sizeof(PROC_QUEUE), &found); if (!queue) return NULL; if (!found) ProcQueueInit(queue); return queue;}#endif/* * ProcQueueInit -- initialize a shared memory process queue */voidProcQueueInit(PROC_QUEUE *queue){ SHMQueueInit(&(queue->links)); queue->size = 0;}/* * ProcSleep -- put a process to sleep * * P() on the semaphore should put us to sleep. The process * semaphore is cleared by default, so the first time we try
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -