mirror of
https://github.com/postgres/postgres.git
synced 2025-06-05 00:02:04 -04:00
Install a "dead man switch" to allow the postmaster to detect cases where
a backend has done exit(0) or exit(1) without having disengaged itself from shared memory. We are at risk for this whenever third-party code is loaded into a backend, since such code might not know it's supposed to go through proc_exit() instead. Also, it is reported that under Windows there are ways to externally kill a process that cause the status code returned to the postmaster to be indistinguishable from a voluntary exit (thank you, Microsoft). If this does happen then the system is probably hosed --- for instance, the dead session might still be holding locks. So the best recovery method is to treat this like a backend crash. The dead man switch is armed for a particular child process when it acquires a regular PGPROC, and disarmed when the PGPROC is released; these should be the first and last touches of shared memory resources in a backend, or close enough anyway. This choice means there is no coverage for auxiliary processes, but I doubt we need that, since they shouldn't be executing any user-provided code anyway. This patch also improves the management of the EXEC_BACKEND ShmemBackendArray array a bit, by reducing search costs. Although this problem is of long standing, the lack of field complaints seems to mean it's not critical enough to risk back-patching; at least not till we get some more testing of this mechanism.
This commit is contained in:
parent
8f348112f3
commit
969d7cd431
@ -37,7 +37,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.580 2009/05/04 02:46:36 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.581 2009/05/05 19:59:00 tgl Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
*
|
*
|
||||||
@ -135,12 +135,14 @@
|
|||||||
* Also, "dead_end" children are in it: these are children launched just
|
* Also, "dead_end" children are in it: these are children launched just
|
||||||
* for the purpose of sending a friendly rejection message to a would-be
|
* for the purpose of sending a friendly rejection message to a would-be
|
||||||
* client. We must track them because they are attached to shared memory,
|
* client. We must track them because they are attached to shared memory,
|
||||||
* but we know they will never become live backends.
|
* but we know they will never become live backends. dead_end children are
|
||||||
|
* not assigned a PMChildSlot.
|
||||||
*/
|
*/
|
||||||
typedef struct bkend
|
typedef struct bkend
|
||||||
{
|
{
|
||||||
pid_t pid; /* process id of backend */
|
pid_t pid; /* process id of backend */
|
||||||
long cancel_key; /* cancel key for cancels for this backend */
|
long cancel_key; /* cancel key for cancels for this backend */
|
||||||
|
int child_slot; /* PMChildSlot for this backend, if any */
|
||||||
bool is_autovacuum; /* is it an autovacuum process? */
|
bool is_autovacuum; /* is it an autovacuum process? */
|
||||||
bool dead_end; /* is it going to send an error and quit? */
|
bool dead_end; /* is it going to send an error and quit? */
|
||||||
Dlelem elem; /* list link in BackendList */
|
Dlelem elem; /* list link in BackendList */
|
||||||
@ -149,15 +151,6 @@ typedef struct bkend
|
|||||||
static Dllist *BackendList;
|
static Dllist *BackendList;
|
||||||
|
|
||||||
#ifdef EXEC_BACKEND
|
#ifdef EXEC_BACKEND
|
||||||
/*
|
|
||||||
* Number of entries in the shared-memory backend table. This table is used
|
|
||||||
* only for sending cancels, and therefore only includes children we allow
|
|
||||||
* cancels on: regular backends and autovac workers. In particular we exclude
|
|
||||||
* dead_end children, allowing the table to have a known maximum size, to wit
|
|
||||||
* the same too-many-children limit enforced by canAcceptConnections().
|
|
||||||
*/
|
|
||||||
#define NUM_BACKENDARRAY_ELEMS (2*MaxBackends)
|
|
||||||
|
|
||||||
static Backend *ShmemBackendArray;
|
static Backend *ShmemBackendArray;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -404,6 +397,7 @@ typedef struct
|
|||||||
char DataDir[MAXPGPATH];
|
char DataDir[MAXPGPATH];
|
||||||
int ListenSocket[MAXLISTEN];
|
int ListenSocket[MAXLISTEN];
|
||||||
long MyCancelKey;
|
long MyCancelKey;
|
||||||
|
int MyPMChildSlot;
|
||||||
unsigned long UsedShmemSegID;
|
unsigned long UsedShmemSegID;
|
||||||
void *UsedShmemSegAddr;
|
void *UsedShmemSegAddr;
|
||||||
slock_t *ShmemLock;
|
slock_t *ShmemLock;
|
||||||
@ -413,6 +407,7 @@ typedef struct
|
|||||||
slock_t *ProcStructLock;
|
slock_t *ProcStructLock;
|
||||||
PROC_HDR *ProcGlobal;
|
PROC_HDR *ProcGlobal;
|
||||||
PGPROC *AuxiliaryProcs;
|
PGPROC *AuxiliaryProcs;
|
||||||
|
PMSignalData *PMSignalState;
|
||||||
InheritableSocket pgStatSock;
|
InheritableSocket pgStatSock;
|
||||||
pid_t PostmasterPid;
|
pid_t PostmasterPid;
|
||||||
TimestampTz PgStartTime;
|
TimestampTz PgStartTime;
|
||||||
@ -443,7 +438,7 @@ static bool save_backend_variables(BackendParameters * param, Port *port,
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void ShmemBackendArrayAdd(Backend *bn);
|
static void ShmemBackendArrayAdd(Backend *bn);
|
||||||
static void ShmemBackendArrayRemove(pid_t pid);
|
static void ShmemBackendArrayRemove(Backend *bn);
|
||||||
#endif /* EXEC_BACKEND */
|
#endif /* EXEC_BACKEND */
|
||||||
|
|
||||||
#define StartupDataBase() StartChildProcess(StartupProcess)
|
#define StartupDataBase() StartChildProcess(StartupProcess)
|
||||||
@ -1771,7 +1766,7 @@ processCancelRequest(Port *port, void *pkt)
|
|||||||
{
|
{
|
||||||
bp = (Backend *) DLE_VAL(curr);
|
bp = (Backend *) DLE_VAL(curr);
|
||||||
#else
|
#else
|
||||||
for (i = 0; i < NUM_BACKENDARRAY_ELEMS; i++)
|
for (i = MaxLivePostmasterChildren() - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
bp = (Backend *) &ShmemBackendArray[i];
|
bp = (Backend *) &ShmemBackendArray[i];
|
||||||
#endif
|
#endif
|
||||||
@ -1836,10 +1831,10 @@ canAcceptConnections(void)
|
|||||||
* MaxBackends limit is enforced when a new backend tries to join the
|
* MaxBackends limit is enforced when a new backend tries to join the
|
||||||
* shared-inval backend array.
|
* shared-inval backend array.
|
||||||
*
|
*
|
||||||
* In the EXEC_BACKEND case, the limit here must match the size of the
|
* The limit here must match the sizes of the per-child-process arrays;
|
||||||
* ShmemBackendArray, since all these processes will have cancel codes.
|
* see comments for MaxLivePostmasterChildren().
|
||||||
*/
|
*/
|
||||||
if (CountChildren() >= 2 * MaxBackends)
|
if (CountChildren() >= MaxLivePostmasterChildren())
|
||||||
return CAC_TOOMANY;
|
return CAC_TOOMANY;
|
||||||
|
|
||||||
return CAC_OK;
|
return CAC_OK;
|
||||||
@ -2439,8 +2434,8 @@ CleanupBackend(int pid,
|
|||||||
/*
|
/*
|
||||||
* If a backend dies in an ugly way then we must signal all other backends
|
* If a backend dies in an ugly way then we must signal all other backends
|
||||||
* to quickdie. If exit status is zero (normal) or one (FATAL exit), we
|
* to quickdie. If exit status is zero (normal) or one (FATAL exit), we
|
||||||
* assume everything is all right and simply remove the backend from the
|
* assume everything is all right and proceed to remove the backend from
|
||||||
* active backend list.
|
* the active backend list.
|
||||||
*/
|
*/
|
||||||
if (!EXIT_STATUS_0(exitstatus) && !EXIT_STATUS_1(exitstatus))
|
if (!EXIT_STATUS_0(exitstatus) && !EXIT_STATUS_1(exitstatus))
|
||||||
{
|
{
|
||||||
@ -2454,10 +2449,21 @@ CleanupBackend(int pid,
|
|||||||
|
|
||||||
if (bp->pid == pid)
|
if (bp->pid == pid)
|
||||||
{
|
{
|
||||||
#ifdef EXEC_BACKEND
|
|
||||||
if (!bp->dead_end)
|
if (!bp->dead_end)
|
||||||
ShmemBackendArrayRemove(pid);
|
{
|
||||||
|
if (!ReleasePostmasterChildSlot(bp->child_slot))
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Uh-oh, the child failed to clean itself up. Treat
|
||||||
|
* as a crash after all.
|
||||||
|
*/
|
||||||
|
HandleChildCrash(pid, exitstatus, _("server process"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#ifdef EXEC_BACKEND
|
||||||
|
ShmemBackendArrayRemove(bp);
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
DLRemove(curr);
|
DLRemove(curr);
|
||||||
free(bp);
|
free(bp);
|
||||||
break;
|
break;
|
||||||
@ -2500,10 +2506,13 @@ HandleChildCrash(int pid, int exitstatus, const char *procname)
|
|||||||
/*
|
/*
|
||||||
* Found entry for freshly-dead backend, so remove it.
|
* Found entry for freshly-dead backend, so remove it.
|
||||||
*/
|
*/
|
||||||
#ifdef EXEC_BACKEND
|
|
||||||
if (!bp->dead_end)
|
if (!bp->dead_end)
|
||||||
ShmemBackendArrayRemove(pid);
|
{
|
||||||
|
(void) ReleasePostmasterChildSlot(bp->child_slot);
|
||||||
|
#ifdef EXEC_BACKEND
|
||||||
|
ShmemBackendArrayRemove(bp);
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
DLRemove(curr);
|
DLRemove(curr);
|
||||||
free(bp);
|
free(bp);
|
||||||
/* Keep looping so we can signal remaining backends */
|
/* Keep looping so we can signal remaining backends */
|
||||||
@ -2931,14 +2940,7 @@ BackendStartup(Port *port)
|
|||||||
pid_t pid;
|
pid_t pid;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Compute the cancel key that will be assigned to this backend. The
|
* Create backend data structure. Better before the fork() so we
|
||||||
* backend will have its own copy in the forked-off process' value of
|
|
||||||
* MyCancelKey, so that it can transmit the key to the frontend.
|
|
||||||
*/
|
|
||||||
MyCancelKey = PostmasterRandom();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Make room for backend data structure. Better before the fork() so we
|
|
||||||
* can handle failure cleanly.
|
* can handle failure cleanly.
|
||||||
*/
|
*/
|
||||||
bn = (Backend *) malloc(sizeof(Backend));
|
bn = (Backend *) malloc(sizeof(Backend));
|
||||||
@ -2950,8 +2952,26 @@ BackendStartup(Port *port)
|
|||||||
return STATUS_ERROR;
|
return STATUS_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compute the cancel key that will be assigned to this backend. The
|
||||||
|
* backend will have its own copy in the forked-off process' value of
|
||||||
|
* MyCancelKey, so that it can transmit the key to the frontend.
|
||||||
|
*/
|
||||||
|
MyCancelKey = PostmasterRandom();
|
||||||
|
bn->cancel_key = MyCancelKey;
|
||||||
|
|
||||||
/* Pass down canAcceptConnections state */
|
/* Pass down canAcceptConnections state */
|
||||||
port->canAcceptConnections = canAcceptConnections();
|
port->canAcceptConnections = canAcceptConnections();
|
||||||
|
bn->dead_end = (port->canAcceptConnections != CAC_OK &&
|
||||||
|
port->canAcceptConnections != CAC_WAITBACKUP);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Unless it's a dead_end child, assign it a child slot number
|
||||||
|
*/
|
||||||
|
if (!bn->dead_end)
|
||||||
|
bn->child_slot = MyPMChildSlot = AssignPostmasterChildSlot();
|
||||||
|
else
|
||||||
|
bn->child_slot = 0;
|
||||||
|
|
||||||
#ifdef EXEC_BACKEND
|
#ifdef EXEC_BACKEND
|
||||||
pid = backend_forkexec(port);
|
pid = backend_forkexec(port);
|
||||||
@ -3009,10 +3029,7 @@ BackendStartup(Port *port)
|
|||||||
* of backends.
|
* of backends.
|
||||||
*/
|
*/
|
||||||
bn->pid = pid;
|
bn->pid = pid;
|
||||||
bn->cancel_key = MyCancelKey;
|
|
||||||
bn->is_autovacuum = false;
|
bn->is_autovacuum = false;
|
||||||
bn->dead_end = (port->canAcceptConnections != CAC_OK &&
|
|
||||||
port->canAcceptConnections != CAC_WAITBACKUP);
|
|
||||||
DLInitElem(&bn->elem, bn);
|
DLInitElem(&bn->elem, bn);
|
||||||
DLAddHead(BackendList, &bn->elem);
|
DLAddHead(BackendList, &bn->elem);
|
||||||
#ifdef EXEC_BACKEND
|
#ifdef EXEC_BACKEND
|
||||||
@ -4270,6 +4287,9 @@ StartAutovacuumWorker(void)
|
|||||||
* changes.
|
* changes.
|
||||||
*/
|
*/
|
||||||
if (canAcceptConnections() == CAC_OK)
|
if (canAcceptConnections() == CAC_OK)
|
||||||
|
{
|
||||||
|
bn = (Backend *) malloc(sizeof(Backend));
|
||||||
|
if (bn)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Compute the cancel key that will be assigned to this session. We
|
* Compute the cancel key that will be assigned to this session. We
|
||||||
@ -4278,16 +4298,16 @@ StartAutovacuumWorker(void)
|
|||||||
* people from sending cancels to them.
|
* people from sending cancels to them.
|
||||||
*/
|
*/
|
||||||
MyCancelKey = PostmasterRandom();
|
MyCancelKey = PostmasterRandom();
|
||||||
|
bn->cancel_key = MyCancelKey;
|
||||||
|
|
||||||
|
/* Autovac workers are not dead_end and need a child slot */
|
||||||
|
bn->dead_end = false;
|
||||||
|
bn->child_slot = MyPMChildSlot = AssignPostmasterChildSlot();
|
||||||
|
|
||||||
bn = (Backend *) malloc(sizeof(Backend));
|
|
||||||
if (bn)
|
|
||||||
{
|
|
||||||
bn->pid = StartAutoVacWorker();
|
bn->pid = StartAutoVacWorker();
|
||||||
if (bn->pid > 0)
|
if (bn->pid > 0)
|
||||||
{
|
{
|
||||||
bn->cancel_key = MyCancelKey;
|
|
||||||
bn->is_autovacuum = true;
|
bn->is_autovacuum = true;
|
||||||
bn->dead_end = false;
|
|
||||||
DLInitElem(&bn->elem, bn);
|
DLInitElem(&bn->elem, bn);
|
||||||
DLAddHead(BackendList, &bn->elem);
|
DLAddHead(BackendList, &bn->elem);
|
||||||
#ifdef EXEC_BACKEND
|
#ifdef EXEC_BACKEND
|
||||||
@ -4353,6 +4373,24 @@ CreateOptsFile(int argc, char *argv[], char *fullprogname)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MaxLivePostmasterChildren
|
||||||
|
*
|
||||||
|
* This reports the number of entries needed in per-child-process arrays
|
||||||
|
* (the PMChildFlags array, and if EXEC_BACKEND the ShmemBackendArray).
|
||||||
|
* These arrays include regular backends and autovac workers, but not special
|
||||||
|
* children nor dead_end children. This allows the arrays to have a fixed
|
||||||
|
* maximum size, to wit the same too-many-children limit enforced by
|
||||||
|
* canAcceptConnections(). The exact value isn't too critical as long as
|
||||||
|
* it's more than MaxBackends.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
MaxLivePostmasterChildren(void)
|
||||||
|
{
|
||||||
|
return 2 * MaxBackends;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef EXEC_BACKEND
|
#ifdef EXEC_BACKEND
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -4364,6 +4402,7 @@ extern LWLock *LWLockArray;
|
|||||||
extern slock_t *ProcStructLock;
|
extern slock_t *ProcStructLock;
|
||||||
extern PROC_HDR *ProcGlobal;
|
extern PROC_HDR *ProcGlobal;
|
||||||
extern PGPROC *AuxiliaryProcs;
|
extern PGPROC *AuxiliaryProcs;
|
||||||
|
extern PMSignalData *PMSignalState;
|
||||||
extern int pgStatSock;
|
extern int pgStatSock;
|
||||||
|
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
@ -4395,6 +4434,7 @@ save_backend_variables(BackendParameters * param, Port *port,
|
|||||||
memcpy(¶m->ListenSocket, &ListenSocket, sizeof(ListenSocket));
|
memcpy(¶m->ListenSocket, &ListenSocket, sizeof(ListenSocket));
|
||||||
|
|
||||||
param->MyCancelKey = MyCancelKey;
|
param->MyCancelKey = MyCancelKey;
|
||||||
|
param->MyPMChildSlot = MyPMChildSlot;
|
||||||
|
|
||||||
param->UsedShmemSegID = UsedShmemSegID;
|
param->UsedShmemSegID = UsedShmemSegID;
|
||||||
param->UsedShmemSegAddr = UsedShmemSegAddr;
|
param->UsedShmemSegAddr = UsedShmemSegAddr;
|
||||||
@ -4407,6 +4447,7 @@ save_backend_variables(BackendParameters * param, Port *port,
|
|||||||
param->ProcStructLock = ProcStructLock;
|
param->ProcStructLock = ProcStructLock;
|
||||||
param->ProcGlobal = ProcGlobal;
|
param->ProcGlobal = ProcGlobal;
|
||||||
param->AuxiliaryProcs = AuxiliaryProcs;
|
param->AuxiliaryProcs = AuxiliaryProcs;
|
||||||
|
param->PMSignalState = PMSignalState;
|
||||||
write_inheritable_socket(¶m->pgStatSock, pgStatSock, childPid);
|
write_inheritable_socket(¶m->pgStatSock, pgStatSock, childPid);
|
||||||
|
|
||||||
param->PostmasterPid = PostmasterPid;
|
param->PostmasterPid = PostmasterPid;
|
||||||
@ -4601,6 +4642,7 @@ restore_backend_variables(BackendParameters * param, Port *port)
|
|||||||
memcpy(&ListenSocket, ¶m->ListenSocket, sizeof(ListenSocket));
|
memcpy(&ListenSocket, ¶m->ListenSocket, sizeof(ListenSocket));
|
||||||
|
|
||||||
MyCancelKey = param->MyCancelKey;
|
MyCancelKey = param->MyCancelKey;
|
||||||
|
MyPMChildSlot = param->MyPMChildSlot;
|
||||||
|
|
||||||
UsedShmemSegID = param->UsedShmemSegID;
|
UsedShmemSegID = param->UsedShmemSegID;
|
||||||
UsedShmemSegAddr = param->UsedShmemSegAddr;
|
UsedShmemSegAddr = param->UsedShmemSegAddr;
|
||||||
@ -4613,6 +4655,7 @@ restore_backend_variables(BackendParameters * param, Port *port)
|
|||||||
ProcStructLock = param->ProcStructLock;
|
ProcStructLock = param->ProcStructLock;
|
||||||
ProcGlobal = param->ProcGlobal;
|
ProcGlobal = param->ProcGlobal;
|
||||||
AuxiliaryProcs = param->AuxiliaryProcs;
|
AuxiliaryProcs = param->AuxiliaryProcs;
|
||||||
|
PMSignalState = param->PMSignalState;
|
||||||
read_inheritable_socket(&pgStatSock, ¶m->pgStatSock);
|
read_inheritable_socket(&pgStatSock, ¶m->pgStatSock);
|
||||||
|
|
||||||
PostmasterPid = param->PostmasterPid;
|
PostmasterPid = param->PostmasterPid;
|
||||||
@ -4642,7 +4685,7 @@ restore_backend_variables(BackendParameters * param, Port *port)
|
|||||||
Size
|
Size
|
||||||
ShmemBackendArraySize(void)
|
ShmemBackendArraySize(void)
|
||||||
{
|
{
|
||||||
return mul_size(NUM_BACKENDARRAY_ELEMS, sizeof(Backend));
|
return mul_size(MaxLivePostmasterChildren(), sizeof(Backend));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -4658,41 +4701,23 @@ ShmemBackendArrayAllocation(void)
|
|||||||
static void
|
static void
|
||||||
ShmemBackendArrayAdd(Backend *bn)
|
ShmemBackendArrayAdd(Backend *bn)
|
||||||
{
|
{
|
||||||
int i;
|
/* The array slot corresponding to my PMChildSlot should be free */
|
||||||
|
int i = bn->child_slot - 1;
|
||||||
|
|
||||||
/* Find an empty slot */
|
Assert(ShmemBackendArray[i].pid == 0);
|
||||||
for (i = 0; i < NUM_BACKENDARRAY_ELEMS; i++)
|
|
||||||
{
|
|
||||||
if (ShmemBackendArray[i].pid == 0)
|
|
||||||
{
|
|
||||||
ShmemBackendArray[i] = *bn;
|
ShmemBackendArray[i] = *bn;
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ereport(FATAL,
|
|
||||||
(errmsg_internal("no free slots in shmem backend array")));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
ShmemBackendArrayRemove(pid_t pid)
|
ShmemBackendArrayRemove(Backend *bn)
|
||||||
{
|
{
|
||||||
int i;
|
int i = bn->child_slot - 1;
|
||||||
|
|
||||||
for (i = 0; i < NUM_BACKENDARRAY_ELEMS; i++)
|
Assert(ShmemBackendArray[i].pid == bn->pid);
|
||||||
{
|
|
||||||
if (ShmemBackendArray[i].pid == pid)
|
|
||||||
{
|
|
||||||
/* Mark the slot as empty */
|
/* Mark the slot as empty */
|
||||||
ShmemBackendArray[i].pid = 0;
|
ShmemBackendArray[i].pid = 0;
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ereport(WARNING,
|
|
||||||
(errmsg_internal("could not find backend entry with pid %d",
|
|
||||||
(int) pid)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* EXEC_BACKEND */
|
#endif /* EXEC_BACKEND */
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/storage/ipc/ipci.c,v 1.99 2009/01/03 17:08:39 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/storage/ipc/ipci.c,v 1.100 2009/05/05 19:59:00 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -111,6 +111,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
|
|||||||
size = add_size(size, ProcArrayShmemSize());
|
size = add_size(size, ProcArrayShmemSize());
|
||||||
size = add_size(size, BackendStatusShmemSize());
|
size = add_size(size, BackendStatusShmemSize());
|
||||||
size = add_size(size, SInvalShmemSize());
|
size = add_size(size, SInvalShmemSize());
|
||||||
|
size = add_size(size, PMSignalShmemSize());
|
||||||
size = add_size(size, BgWriterShmemSize());
|
size = add_size(size, BgWriterShmemSize());
|
||||||
size = add_size(size, AutoVacuumShmemSize());
|
size = add_size(size, AutoVacuumShmemSize());
|
||||||
size = add_size(size, BTreeShmemSize());
|
size = add_size(size, BTreeShmemSize());
|
||||||
@ -206,7 +207,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
|
|||||||
/*
|
/*
|
||||||
* Set up interprocess signaling mechanisms
|
* Set up interprocess signaling mechanisms
|
||||||
*/
|
*/
|
||||||
PMSignalInit();
|
PMSignalShmemInit();
|
||||||
BgWriterShmemInit();
|
BgWriterShmemInit();
|
||||||
AutoVacuumShmemInit();
|
AutoVacuumShmemInit();
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/storage/ipc/pmsignal.c,v 1.26 2009/01/01 17:23:47 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/storage/ipc/pmsignal.c,v 1.27 2009/05/05 19:59:00 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -34,26 +34,69 @@
|
|||||||
* The flags are actually declared as "volatile sig_atomic_t" for maximum
|
* The flags are actually declared as "volatile sig_atomic_t" for maximum
|
||||||
* portability. This should ensure that loads and stores of the flag
|
* portability. This should ensure that loads and stores of the flag
|
||||||
* values are atomic, allowing us to dispense with any explicit locking.
|
* values are atomic, allowing us to dispense with any explicit locking.
|
||||||
|
*
|
||||||
|
* In addition to the per-reason flags, we store a set of per-child-process
|
||||||
|
* flags that are currently used only for detecting whether a backend has
|
||||||
|
* exited without performing proper shutdown. The per-child-process flags
|
||||||
|
* have three possible states: UNUSED, ASSIGNED, ACTIVE. An UNUSED slot is
|
||||||
|
* available for assignment. An ASSIGNED slot is associated with a postmaster
|
||||||
|
* child process, but either the process has not touched shared memory yet,
|
||||||
|
* or it has successfully cleaned up after itself. A ACTIVE slot means the
|
||||||
|
* process is actively using shared memory. The slots are assigned to
|
||||||
|
* child processes at random, and postmaster.c is responsible for tracking
|
||||||
|
* which one goes with which PID.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static volatile sig_atomic_t *PMSignalFlags;
|
#define PM_CHILD_UNUSED 0 /* these values must fit in sig_atomic_t */
|
||||||
|
#define PM_CHILD_ASSIGNED 1
|
||||||
|
#define PM_CHILD_ACTIVE 2
|
||||||
|
|
||||||
|
/* "typedef struct PMSignalData PMSignalData" appears in pmsignal.h */
|
||||||
|
struct PMSignalData
|
||||||
|
{
|
||||||
|
/* per-reason flags */
|
||||||
|
sig_atomic_t PMSignalFlags[NUM_PMSIGNALS];
|
||||||
|
/* per-child-process flags */
|
||||||
|
int num_child_flags; /* # of entries in PMChildFlags[] */
|
||||||
|
int next_child_flag; /* next slot to try to assign */
|
||||||
|
sig_atomic_t PMChildFlags[1]; /* VARIABLE LENGTH ARRAY */
|
||||||
|
};
|
||||||
|
|
||||||
|
NON_EXEC_STATIC volatile PMSignalData *PMSignalState = NULL;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* PMSignalInit - initialize during shared-memory creation
|
* PMSignalShmemSize
|
||||||
|
* Compute space needed for pmsignal.c's shared memory
|
||||||
|
*/
|
||||||
|
Size
|
||||||
|
PMSignalShmemSize(void)
|
||||||
|
{
|
||||||
|
Size size;
|
||||||
|
|
||||||
|
size = offsetof(PMSignalData, PMChildFlags);
|
||||||
|
size = add_size(size, mul_size(MaxLivePostmasterChildren(),
|
||||||
|
sizeof(sig_atomic_t)));
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PMSignalShmemInit - initialize during shared-memory creation
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
PMSignalInit(void)
|
PMSignalShmemInit(void)
|
||||||
{
|
{
|
||||||
bool found;
|
bool found;
|
||||||
|
|
||||||
PMSignalFlags = (sig_atomic_t *)
|
PMSignalState = (PMSignalData *)
|
||||||
ShmemInitStruct("PMSignalFlags",
|
ShmemInitStruct("PMSignalState", PMSignalShmemSize(), &found);
|
||||||
NUM_PMSIGNALS * sizeof(sig_atomic_t),
|
|
||||||
&found);
|
|
||||||
|
|
||||||
if (!found)
|
if (!found)
|
||||||
MemSet(PMSignalFlags, 0, NUM_PMSIGNALS * sizeof(sig_atomic_t));
|
{
|
||||||
|
MemSet(PMSignalState, 0, PMSignalShmemSize());
|
||||||
|
PMSignalState->num_child_flags = MaxLivePostmasterChildren();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -66,7 +109,7 @@ SendPostmasterSignal(PMSignalReason reason)
|
|||||||
if (!IsUnderPostmaster)
|
if (!IsUnderPostmaster)
|
||||||
return;
|
return;
|
||||||
/* Atomically set the proper flag */
|
/* Atomically set the proper flag */
|
||||||
PMSignalFlags[reason] = true;
|
PMSignalState->PMSignalFlags[reason] = true;
|
||||||
/* Send signal to postmaster */
|
/* Send signal to postmaster */
|
||||||
kill(PostmasterPid, SIGUSR1);
|
kill(PostmasterPid, SIGUSR1);
|
||||||
}
|
}
|
||||||
@ -80,14 +123,105 @@ bool
|
|||||||
CheckPostmasterSignal(PMSignalReason reason)
|
CheckPostmasterSignal(PMSignalReason reason)
|
||||||
{
|
{
|
||||||
/* Careful here --- don't clear flag if we haven't seen it set */
|
/* Careful here --- don't clear flag if we haven't seen it set */
|
||||||
if (PMSignalFlags[reason])
|
if (PMSignalState->PMSignalFlags[reason])
|
||||||
{
|
{
|
||||||
PMSignalFlags[reason] = false;
|
PMSignalState->PMSignalFlags[reason] = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AssignPostmasterChildSlot - select an unused slot for a new postmaster
|
||||||
|
* child process, and set its state to ASSIGNED. Returns a slot number
|
||||||
|
* (one to N).
|
||||||
|
*
|
||||||
|
* Only the postmaster is allowed to execute this routine, so we need no
|
||||||
|
* special locking.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
AssignPostmasterChildSlot(void)
|
||||||
|
{
|
||||||
|
int slot = PMSignalState->next_child_flag;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Scan for a free slot. We track the last slot assigned so as not to
|
||||||
|
* waste time repeatedly rescanning low-numbered slots.
|
||||||
|
*/
|
||||||
|
for (n = PMSignalState->num_child_flags; n > 0; n--)
|
||||||
|
{
|
||||||
|
if (--slot < 0)
|
||||||
|
slot = PMSignalState->num_child_flags - 1;
|
||||||
|
if (PMSignalState->PMChildFlags[slot] == PM_CHILD_UNUSED)
|
||||||
|
{
|
||||||
|
PMSignalState->PMChildFlags[slot] = PM_CHILD_ASSIGNED;
|
||||||
|
PMSignalState->next_child_flag = slot;
|
||||||
|
return slot + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Out of slots ... should never happen, else postmaster.c messed up */
|
||||||
|
elog(FATAL, "no free slots in PMChildFlags array");
|
||||||
|
return 0; /* keep compiler quiet */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ReleasePostmasterChildSlot - release a slot after death of a postmaster
|
||||||
|
* child process. This must be called in the postmaster process.
|
||||||
|
*
|
||||||
|
* Returns true if the slot had been in ASSIGNED state (the expected case),
|
||||||
|
* false otherwise (implying that the child failed to clean itself up).
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
ReleasePostmasterChildSlot(int slot)
|
||||||
|
{
|
||||||
|
bool result;
|
||||||
|
|
||||||
|
Assert(slot > 0 && slot <= PMSignalState->num_child_flags);
|
||||||
|
slot--;
|
||||||
|
/*
|
||||||
|
* Note: the slot state might already be unused, because the logic in
|
||||||
|
* postmaster.c is such that this might get called twice when a child
|
||||||
|
* crashes. So we don't try to Assert anything about the state.
|
||||||
|
*/
|
||||||
|
result = (PMSignalState->PMChildFlags[slot] == PM_CHILD_ASSIGNED);
|
||||||
|
PMSignalState->PMChildFlags[slot] = PM_CHILD_UNUSED;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MarkPostmasterChildActive - mark a postmaster child as about to begin
|
||||||
|
* actively using shared memory. This is called in the child process.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
MarkPostmasterChildActive(void)
|
||||||
|
{
|
||||||
|
int slot = MyPMChildSlot;
|
||||||
|
|
||||||
|
Assert(slot > 0 && slot <= PMSignalState->num_child_flags);
|
||||||
|
slot--;
|
||||||
|
Assert(PMSignalState->PMChildFlags[slot] == PM_CHILD_ASSIGNED);
|
||||||
|
PMSignalState->PMChildFlags[slot] = PM_CHILD_ACTIVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MarkPostmasterChildInactive - mark a postmaster child as done using
|
||||||
|
* shared memory. This is called in the child process.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
MarkPostmasterChildInactive(void)
|
||||||
|
{
|
||||||
|
int slot = MyPMChildSlot;
|
||||||
|
|
||||||
|
Assert(slot > 0 && slot <= PMSignalState->num_child_flags);
|
||||||
|
slot--;
|
||||||
|
Assert(PMSignalState->PMChildFlags[slot] == PM_CHILD_ACTIVE);
|
||||||
|
PMSignalState->PMChildFlags[slot] = PM_CHILD_ASSIGNED;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* PostmasterIsAlive - check whether postmaster process is still alive
|
* PostmasterIsAlive - check whether postmaster process is still alive
|
||||||
*
|
*
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/storage/lmgr/proc.c,v 1.205 2009/01/01 17:23:48 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/storage/lmgr/proc.c,v 1.206 2009/05/05 19:59:00 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -41,6 +41,7 @@
|
|||||||
#include "postmaster/autovacuum.h"
|
#include "postmaster/autovacuum.h"
|
||||||
#include "storage/ipc.h"
|
#include "storage/ipc.h"
|
||||||
#include "storage/lmgr.h"
|
#include "storage/lmgr.h"
|
||||||
|
#include "storage/pmsignal.h"
|
||||||
#include "storage/proc.h"
|
#include "storage/proc.h"
|
||||||
#include "storage/procarray.h"
|
#include "storage/procarray.h"
|
||||||
#include "storage/spin.h"
|
#include "storage/spin.h"
|
||||||
@ -274,6 +275,14 @@ InitProcess(void)
|
|||||||
errmsg("sorry, too many clients already")));
|
errmsg("sorry, too many clients already")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now that we have a PGPROC, mark ourselves as an active postmaster
|
||||||
|
* child; this is so that the postmaster can detect it if we exit
|
||||||
|
* without cleaning up.
|
||||||
|
*/
|
||||||
|
if (IsUnderPostmaster)
|
||||||
|
MarkPostmasterChildActive();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize all fields of MyProc, except for the semaphore which was
|
* Initialize all fields of MyProc, except for the semaphore which was
|
||||||
* prepared for us by InitProcGlobal.
|
* prepared for us by InitProcGlobal.
|
||||||
@ -614,6 +623,13 @@ ProcKill(int code, Datum arg)
|
|||||||
|
|
||||||
SpinLockRelease(ProcStructLock);
|
SpinLockRelease(ProcStructLock);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This process is no longer present in shared memory in any meaningful
|
||||||
|
* way, so tell the postmaster we've cleaned up acceptably well.
|
||||||
|
*/
|
||||||
|
if (IsUnderPostmaster)
|
||||||
|
MarkPostmasterChildInactive();
|
||||||
|
|
||||||
/* wake autovac launcher if needed -- see comments in FreeWorkerInfo */
|
/* wake autovac launcher if needed -- see comments in FreeWorkerInfo */
|
||||||
if (AutovacuumLauncherPid != 0)
|
if (AutovacuumLauncherPid != 0)
|
||||||
kill(AutovacuumLauncherPid, SIGUSR1);
|
kill(AutovacuumLauncherPid, SIGUSR1);
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/utils/init/globals.c,v 1.107 2009/01/01 17:23:51 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/utils/init/globals.c,v 1.108 2009/05/05 19:59:00 tgl Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* Globals used all over the place should be declared here and not
|
* Globals used all over the place should be declared here and not
|
||||||
@ -36,6 +36,7 @@ int MyProcPid;
|
|||||||
pg_time_t MyStartTime;
|
pg_time_t MyStartTime;
|
||||||
struct Port *MyProcPort;
|
struct Port *MyProcPort;
|
||||||
long MyCancelKey;
|
long MyCancelKey;
|
||||||
|
int MyPMChildSlot;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* DataDir is the absolute path to the top level of the PGDATA directory tree.
|
* DataDir is the absolute path to the top level of the PGDATA directory tree.
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/miscadmin.h,v 1.209 2009/01/05 02:27:45 alvherre Exp $
|
* $PostgreSQL: pgsql/src/include/miscadmin.h,v 1.210 2009/05/05 19:59:00 tgl Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* some of the information in this file should be moved to other files.
|
* some of the information in this file should be moved to other files.
|
||||||
@ -137,6 +137,7 @@ extern PGDLLIMPORT int MyProcPid;
|
|||||||
extern PGDLLIMPORT pg_time_t MyStartTime;
|
extern PGDLLIMPORT pg_time_t MyStartTime;
|
||||||
extern PGDLLIMPORT struct Port *MyProcPort;
|
extern PGDLLIMPORT struct Port *MyProcPort;
|
||||||
extern long MyCancelKey;
|
extern long MyCancelKey;
|
||||||
|
extern int MyPMChildSlot;
|
||||||
|
|
||||||
extern char OutputFileName[];
|
extern char OutputFileName[];
|
||||||
extern PGDLLIMPORT char my_exec_path[];
|
extern PGDLLIMPORT char my_exec_path[];
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/postmaster/postmaster.h,v 1.19 2009/01/01 17:24:01 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/postmaster/postmaster.h,v 1.20 2009/05/05 19:59:00 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -38,6 +38,8 @@ extern const char *progname;
|
|||||||
extern int PostmasterMain(int argc, char *argv[]);
|
extern int PostmasterMain(int argc, char *argv[]);
|
||||||
extern void ClosePostmasterPorts(bool am_syslogger);
|
extern void ClosePostmasterPorts(bool am_syslogger);
|
||||||
|
|
||||||
|
extern int MaxLivePostmasterChildren(void);
|
||||||
|
|
||||||
#ifdef EXEC_BACKEND
|
#ifdef EXEC_BACKEND
|
||||||
extern pid_t postmaster_forkexec(int argc, char *argv[]);
|
extern pid_t postmaster_forkexec(int argc, char *argv[]);
|
||||||
extern int SubPostmasterMain(int argc, char *argv[]);
|
extern int SubPostmasterMain(int argc, char *argv[]);
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/storage/pmsignal.h,v 1.23 2009/02/23 09:28:50 heikki Exp $
|
* $PostgreSQL: pgsql/src/include/storage/pmsignal.h,v 1.24 2009/05/05 19:59:00 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -33,12 +33,20 @@ typedef enum
|
|||||||
NUM_PMSIGNALS /* Must be last value of enum! */
|
NUM_PMSIGNALS /* Must be last value of enum! */
|
||||||
} PMSignalReason;
|
} PMSignalReason;
|
||||||
|
|
||||||
|
/* PMSignalData is an opaque struct, details known only within pmsignal.c */
|
||||||
|
typedef struct PMSignalData PMSignalData;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* prototypes for functions in pmsignal.c
|
* prototypes for functions in pmsignal.c
|
||||||
*/
|
*/
|
||||||
extern void PMSignalInit(void);
|
extern Size PMSignalShmemSize(void);
|
||||||
|
extern void PMSignalShmemInit(void);
|
||||||
extern void SendPostmasterSignal(PMSignalReason reason);
|
extern void SendPostmasterSignal(PMSignalReason reason);
|
||||||
extern bool CheckPostmasterSignal(PMSignalReason reason);
|
extern bool CheckPostmasterSignal(PMSignalReason reason);
|
||||||
|
extern int AssignPostmasterChildSlot(void);
|
||||||
|
extern bool ReleasePostmasterChildSlot(int slot);
|
||||||
|
extern void MarkPostmasterChildActive(void);
|
||||||
|
extern void MarkPostmasterChildInactive(void);
|
||||||
extern bool PostmasterIsAlive(bool amDirectChild);
|
extern bool PostmasterIsAlive(bool amDirectChild);
|
||||||
|
|
||||||
#endif /* PMSIGNAL_H */
|
#endif /* PMSIGNAL_H */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user