1
0
mirror of https://github.com/postgres/postgres.git synced 2025-09-02 04:21:28 +03:00

Separate out bgwriter code into a logically separate module, rather

than being random pieces of other files.  Give bgwriter responsibility
for all checkpoint activity (other than a post-recovery checkpoint);
so this child process absorbs the functionality of the former transient
checkpoint and shutdown subprocesses.  While at it, create an actual
include file for postmaster.c, which for some reason never had its own
file before.
This commit is contained in:
Tom Lane
2004-05-29 22:48:23 +00:00
parent d531fd2cdc
commit 076a055acf
28 changed files with 1133 additions and 902 deletions

View File

@@ -1,10 +1,10 @@
#-------------------------------------------------------------------------
#
# Makefile--
# Makefile for postmaster
# Makefile for src/backend/postmaster
#
# IDENTIFICATION
# $PostgreSQL: pgsql/src/backend/postmaster/Makefile,v 1.14 2003/11/29 19:51:55 pgsql Exp $
# $PostgreSQL: pgsql/src/backend/postmaster/Makefile,v 1.15 2004/05/29 22:48:19 tgl Exp $
#
#-------------------------------------------------------------------------
@@ -12,7 +12,7 @@ subdir = src/backend/postmaster
top_builddir = ../../..
include $(top_builddir)/src/Makefile.global
OBJS = postmaster.o pgstat.o
OBJS = postmaster.o bgwriter.o pgstat.o
all: SUBSYS.o

View File

@@ -0,0 +1,456 @@
/*-------------------------------------------------------------------------
*
* bgwriter.c
*
* The background writer (bgwriter) is new in Postgres 7.5. It attempts
* to keep regular backends from having to write out dirty shared buffers
* (which they would only do when needing to free a shared buffer to read in
* another page). In the best scenario all writes from shared buffers will
* be issued by the background writer process. However, regular backends are
* still empowered to issue writes if the bgwriter fails to maintain enough
* clean shared buffers.
*
* The bgwriter is also charged with handling all checkpoints. It will
* automatically dispatch a checkpoint after a certain amount of time has
* elapsed since the last one, and it can be signaled to perform requested
* checkpoints as well. (The GUC parameter that mandates a checkpoint every
* so many WAL segments is implemented by having backends signal the bgwriter
* when they fill WAL segments; the bgwriter itself doesn't watch for the
* condition.)
*
* The bgwriter is started by the postmaster as soon as the startup subprocess
* finishes. It remains alive until the postmaster commands it to terminate.
* Normal termination is by SIGUSR2, which instructs the bgwriter to execute
* a shutdown checkpoint and then exit(0). (All backends must be stopped
* before SIGUSR2 is issued!) Emergency termination is by SIGQUIT; like any
* backend, the bgwriter will simply abort and exit on SIGQUIT.
*
* If the bgwriter exits unexpectedly, the postmaster treats that the same
* as a backend crash: shared memory may be corrupted, so remaining backends
* should be killed by SIGQUIT and then a recovery cycle started.
*
*
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/postmaster/bgwriter.c,v 1.1 2004/05/29 22:48:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include <signal.h>
#include "access/xlog.h"
#include "libpq/pqsignal.h"
#include "miscadmin.h"
#include "postmaster/bgwriter.h"
#include "storage/bufmgr.h"
#include "storage/freespace.h"
#include "storage/ipc.h"
#include "storage/pmsignal.h"
#include "storage/smgr.h"
#include "tcop/tcopprot.h"
#include "utils/guc.h"
/*
* Shared memory area for communication between bgwriter and backends
*/
typedef struct
{
pid_t bgwriter_pid; /* PID of bgwriter (0 if not started) */
sig_atomic_t checkpoint_count; /* advances when checkpoint done */
} BgWriterShmemStruct;
static BgWriterShmemStruct *BgWriterShmem;
/*
* GUC parameters
*/
int BgWriterDelay = 200;
int BgWriterPercent = 1;
int BgWriterMaxPages = 100;
int CheckPointTimeout = 300;
int CheckPointWarning = 30;
/*
* Flags set by interrupt handlers for later service in the main loop.
*/
static volatile sig_atomic_t got_SIGHUP = false;
static volatile sig_atomic_t checkpoint_requested = false;
static volatile sig_atomic_t shutdown_requested = false;
/*
* Private state
*/
static time_t last_checkpoint_time;
static void bg_quickdie(SIGNAL_ARGS);
static void BgSigHupHandler(SIGNAL_ARGS);
static void ReqCheckpointHandler(SIGNAL_ARGS);
static void ReqShutdownHandler(SIGNAL_ARGS);
/*
* Main entry point for bgwriter process
*
* This is invoked from BootstrapMain, which has already created the basic
* execution environment, but not enabled signals yet.
*/
void
BackgroundWriterMain(void)
{
Assert(BgWriterShmem != NULL);
BgWriterShmem->bgwriter_pid = MyProcPid;
/*
* Properly accept or ignore signals the postmaster might send us
*
* Note: we deliberately ignore SIGTERM, because during a standard Unix
* system shutdown cycle, init will SIGTERM all processes at once. We
* want to wait for the backends to exit, whereupon the postmaster will
* tell us it's okay to shut down (via SIGUSR2).
*
* SIGUSR1 is presently unused; keep it spare in case someday we want
* this process to participate in sinval messaging.
*/
pqsignal(SIGHUP, BgSigHupHandler); /* set flag to read config file */
pqsignal(SIGINT, ReqCheckpointHandler); /* request checkpoint */
pqsignal(SIGTERM, SIG_IGN); /* ignore SIGTERM */
pqsignal(SIGQUIT, bg_quickdie); /* hard crash time */
pqsignal(SIGALRM, SIG_IGN);
pqsignal(SIGPIPE, SIG_IGN);
pqsignal(SIGUSR1, SIG_IGN); /* reserve for sinval */
pqsignal(SIGUSR2, ReqShutdownHandler); /* request shutdown */
/*
* Reset some signals that are accepted by postmaster but not here
*/
pqsignal(SIGCHLD, SIG_DFL);
pqsignal(SIGTTIN, SIG_DFL);
pqsignal(SIGTTOU, SIG_DFL);
pqsignal(SIGCONT, SIG_DFL);
pqsignal(SIGWINCH, SIG_DFL);
/* We allow SIGQUIT (quickdie) at all times */
#ifdef HAVE_SIGPROCMASK
sigdelset(&BlockSig, SIGQUIT);
#else
BlockSig &= ~(sigmask(SIGQUIT));
#endif
/*
* Initialize so that first time-driven checkpoint happens
* at the correct time.
*/
last_checkpoint_time = time(NULL);
/*
* If an exception is encountered, processing resumes here.
*/
if (sigsetjmp(Warn_restart, 1) != 0)
{
/*
* Make sure we're not interrupted while cleaning up. Also forget
* any pending QueryCancel request, since we're aborting anyway.
* Force InterruptHoldoffCount to a known state in case we
* ereport'd from inside a holdoff section.
*/
ImmediateInterruptOK = false;
QueryCancelPending = false;
InterruptHoldoffCount = 1;
CritSectionCount = 0; /* should be unnecessary, but... */
/*
* These operations are really just a minimal subset of
* AbortTransaction(). We don't have very many resources
* to worry about in bgwriter, but we do have LWLocks and buffers.
*/
LWLockReleaseAll();
AbortBufferIO();
UnlockBuffers();
/*
* Clear flag to indicate that we got out of error recovery mode
* successfully. (Flag was set in elog.c before longjmp().)
*/
InError = false;
/*
* Exit interrupt holdoff section we implicitly established above.
*/
RESUME_INTERRUPTS();
/*
* Sleep at least 1 second after any error. A write error is
* likely to be repeated, and we don't want to be filling the
* error logs as fast as we can. (XXX think about ways to make
* progress when the LRU dirty buffer cannot be written...)
*/
pg_usleep(1000000L);
}
Warn_restart_ready = true; /* we can now handle ereport(ERROR) */
/*
* Unblock signals (they were blocked when the postmaster forked us)
*/
PG_SETMASK(&UnBlockSig);
/*
* Loop forever
*/
for (;;)
{
bool do_checkpoint = false;
bool force_checkpoint = false;
time_t now;
int elapsed_secs;
int n;
long udelay;
/*
* Process any signals received recently.
*/
if (got_SIGHUP)
{
got_SIGHUP = false;
ProcessConfigFile(PGC_SIGHUP);
}
if (checkpoint_requested)
{
checkpoint_requested = false;
do_checkpoint = true;
force_checkpoint = true;
}
if (shutdown_requested)
{
ShutdownXLOG(0, 0);
DumpFreeSpaceMap(0, 0);
/* Normal exit from the bgwriter is here */
proc_exit(0); /* done */
}
/*
* Do an unforced checkpoint if too much time has elapsed
* since the last one.
*/
now = time(NULL);
elapsed_secs = now - last_checkpoint_time;
if (elapsed_secs >= CheckPointTimeout)
do_checkpoint = true;
/*
* Do a checkpoint if requested, otherwise do one cycle of
* dirty-buffer writing.
*/
if (do_checkpoint)
{
if (CheckPointWarning != 0)
{
/*
* Ideally we should only warn if this checkpoint was
* requested due to running out of segment files, and not
* if it was manually requested. However we can't tell the
* difference with the current signalling mechanism.
*/
if (elapsed_secs < CheckPointWarning)
ereport(LOG,
(errmsg("checkpoints are occurring too frequently (%d seconds apart)",
elapsed_secs),
errhint("Consider increasing the configuration parameter \"checkpoint_segments\".")));
}
CreateCheckPoint(false, force_checkpoint);
/*
* Note we record the checkpoint start time not end time as
* last_checkpoint_time. This is so that time-driven checkpoints
* happen at a predictable spacing.
*/
last_checkpoint_time = now;
/*
* Indicate checkpoint completion to any waiting backends.
*/
BgWriterShmem->checkpoint_count++;
/*
* After any checkpoint, close all smgr files. This is so we
* won't hang onto smgr references to deleted files indefinitely.
*/
smgrcloseall();
/* Nap for configured time before rechecking */
n = 1;
}
else
{
n = BufferSync(BgWriterPercent, BgWriterMaxPages);
}
/*
* Nap for the configured time or sleep for 10 seconds if
* there was nothing to do at all.
*
* On some platforms, signals won't interrupt the sleep. To ensure
* we respond reasonably promptly when someone signals us,
* break down the sleep into 1-second increments, and check for
* interrupts after each nap.
*/
udelay = ((n > 0) ? BgWriterDelay : 10000) * 1000L;
while (udelay > 1000000L)
{
if (got_SIGHUP || checkpoint_requested || shutdown_requested)
break;
pg_usleep(1000000L);
udelay -= 1000000L;
}
if (!(got_SIGHUP || checkpoint_requested || shutdown_requested))
pg_usleep(udelay);
/*
* Emergency bailout if postmaster has died. This is to avoid the
* necessity for manual cleanup of all postmaster children.
*/
if (!PostmasterIsAlive(true))
exit(1);
}
}
/* --------------------------------
* signal handler routines
* --------------------------------
*/
/*
* bg_quickdie() occurs when signalled SIGQUIT by the postmaster.
*
* Some backend has bought the farm,
* so we need to stop what we're doing and exit.
*/
static void
bg_quickdie(SIGNAL_ARGS)
{
PG_SETMASK(&BlockSig);
/*
* DO NOT proc_exit() -- we're here because shared memory may be
* corrupted, so we don't want to try to clean up our transaction.
* Just nail the windows shut and get out of town.
*
* Note we do exit(1) not exit(0). This is to force the postmaster into
* a system reset cycle if some idiot DBA sends a manual SIGQUIT to a
* random backend. This is necessary precisely because we don't clean
* up our shared memory state.
*/
exit(1);
}
/* SIGHUP: set flag to re-read config file at next convenient time */
static void
BgSigHupHandler(SIGNAL_ARGS)
{
got_SIGHUP = true;
}
/* SIGINT: set flag to run a normal checkpoint right away */
static void
ReqCheckpointHandler(SIGNAL_ARGS)
{
checkpoint_requested = true;
}
/* SIGUSR2: set flag to run a shutdown checkpoint and exit */
static void
ReqShutdownHandler(SIGNAL_ARGS)
{
shutdown_requested = true;
}
/* --------------------------------
* communication with backends
* --------------------------------
*/
/*
* BgWriterShmemSize
* Compute space needed for bgwriter-related shared memory
*/
int
BgWriterShmemSize(void)
{
/*
* This is not worth measuring right now, but may become so after we
* add fsync signaling ...
*/
return MAXALIGN(sizeof(BgWriterShmemStruct));
}
/*
* BgWriterShmemInit
* Allocate and initialize bgwriter-related shared memory
*/
void
BgWriterShmemInit(void)
{
bool found;
BgWriterShmem = (BgWriterShmemStruct *)
ShmemInitStruct("Background Writer Data",
sizeof(BgWriterShmemStruct),
&found);
if (BgWriterShmem == NULL)
ereport(FATAL,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("insufficient shared memory for bgwriter")));
if (found)
return; /* already initialized */
MemSet(BgWriterShmem, 0, sizeof(BgWriterShmemStruct));
}
/*
* RequestCheckpoint
* Called in backend processes to request an immediate checkpoint
*
* If waitforit is true, wait until the checkpoint is completed
* before returning; otherwise, just signal the request and return
* immediately.
*/
void
RequestCheckpoint(bool waitforit)
{
volatile sig_atomic_t *count_ptr = &BgWriterShmem->checkpoint_count;
sig_atomic_t old_count = *count_ptr;
/*
* Send signal to request checkpoint. When waitforit is false,
* we consider failure to send the signal to be nonfatal.
*/
if (BgWriterShmem->bgwriter_pid == 0)
elog(waitforit ? ERROR : LOG,
"could not request checkpoint because bgwriter not running");
if (kill(BgWriterShmem->bgwriter_pid, SIGINT) != 0)
elog(waitforit ? ERROR : LOG,
"could not signal for checkpoint: %m");
/*
* If requested, wait for completion. We detect completion by
* observing a change in checkpoint_count in shared memory.
*/
if (waitforit)
{
while (*count_ptr == old_count)
{
CHECK_FOR_INTERRUPTS();
pg_usleep(1000000L);
}
}
}

View File

@@ -13,7 +13,7 @@
*
* Copyright (c) 2001-2003, PostgreSQL Global Development Group
*
* $PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.72 2004/05/28 05:12:58 tgl Exp $
* $PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.73 2004/05/29 22:48:19 tgl Exp $
* ----------
*/
#include "postgres.h"
@@ -32,23 +32,25 @@
#include "pgstat.h"
#include "access/xact.h"
#include "access/heapam.h"
#include "access/xact.h"
#include "catalog/catname.h"
#include "catalog/pg_shadow.h"
#include "catalog/pg_database.h"
#include "libpq/pqsignal.h"
#include "catalog/pg_shadow.h"
#include "libpq/libpq.h"
#include "libpq/pqsignal.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "utils/memutils.h"
#include "postmaster/postmaster.h"
#include "storage/backendid.h"
#include "storage/ipc.h"
#include "storage/pg_shmem.h"
#include "storage/pmsignal.h"
#include "tcop/tcopprot.h"
#include "utils/rel.h"
#include "utils/hsearch.h"
#include "utils/memutils.h"
#include "utils/ps_status.h"
#include "utils/rel.h"
#include "utils/syscache.h"
@@ -75,8 +77,6 @@ bool pgstat_is_running = false;
NON_EXEC_STATIC int pgStatSock = -1;
static int pgStatPipe[2];
static struct sockaddr_storage pgStatAddr;
static int pgStatPmPipe[2] = {-1, -1};
static int pgStatCollectorPmPipe[2] = {-1, -1};
static int pgStatPid;
static time_t last_pgstat_start_time;
@@ -397,17 +397,6 @@ pgstat_init(void)
goto startup_failed;
}
/*
* Create the pipe that controls the statistics collector shutdown
*/
if (pgpipe(pgStatPmPipe) < 0 || pgpipe(pgStatCollectorPmPipe) < 0)
{
ereport(LOG,
(errcode_for_socket_access(),
errmsg("could not create pipe for statistics collector: %m")));
goto startup_failed;
}
freeaddrinfo_all(hints.ai_family, addrs);
return;
@@ -439,9 +428,9 @@ startup_failed:
static pid_t
pgstat_forkexec(STATS_PROCESS_TYPE procType)
{
char *av[12];
char *av[10];
int ac = 0, bufc = 0, i;
char pgstatBuf[7][32];
char pgstatBuf[2][32];
av[ac++] = "postgres";
@@ -464,11 +453,7 @@ pgstat_forkexec(STATS_PROCESS_TYPE procType)
/* postgres_exec_path is not passed by write_backend_variables */
av[ac++] = postgres_exec_path;
/* Sockets + pipes (those not passed by write_backend_variables) */
snprintf(pgstatBuf[bufc++],32,"%d",pgStatPmPipe[0]);
snprintf(pgstatBuf[bufc++],32,"%d",pgStatPmPipe[1]);
snprintf(pgstatBuf[bufc++],32,"%d",pgStatCollectorPmPipe[0]);
snprintf(pgstatBuf[bufc++],32,"%d",pgStatCollectorPmPipe[1]);
/* Pipe file ids (those not passed by write_backend_variables) */
snprintf(pgstatBuf[bufc++],32,"%d",pgStatPipe[0]);
snprintf(pgstatBuf[bufc++],32,"%d",pgStatPipe[1]);
@@ -493,14 +478,10 @@ pgstat_forkexec(STATS_PROCESS_TYPE procType)
static void
pgstat_parseArgs(int argc, char *argv[])
{
Assert(argc == 10);
Assert(argc == 6);
argc = 3;
StrNCpy(postgres_exec_path, argv[argc++], MAXPGPATH);
pgStatPmPipe[0] = atoi(argv[argc++]);
pgStatPmPipe[1] = atoi(argv[argc++]);
pgStatCollectorPmPipe[0] = atoi(argv[argc++]);
pgStatCollectorPmPipe[1] = atoi(argv[argc++]);
pgStatPipe[0] = atoi(argv[argc++]);
pgStatPipe[1] = atoi(argv[argc++]);
}
@@ -592,8 +573,8 @@ pgstat_start(void)
/* Specific beos actions after backend startup */
beos_backend_startup();
#endif
/* Close the postmaster's sockets, except for pgstat link */
ClosePostmasterPorts(false);
/* Close the postmaster's sockets */
ClosePostmasterPorts();
/* Drop our connection to postmaster's shared memory, as well */
PGSharedMemoryDetach();
@@ -632,31 +613,6 @@ pgstat_ispgstat(int pid)
}
/* ----------
* pgstat_close_sockets() -
*
* Called when postmaster forks a non-pgstat child process, to close off
* file descriptors that should not be held open in child processes.
* ----------
*/
void
pgstat_close_sockets(void)
{
if (pgStatPmPipe[0] >= 0)
closesocket(pgStatPmPipe[0]);
pgStatPmPipe[0] = -1;
if (pgStatPmPipe[1] >= 0)
closesocket(pgStatPmPipe[1]);
pgStatPmPipe[1] = -1;
if (pgStatCollectorPmPipe[0] >= 0)
closesocket(pgStatCollectorPmPipe[0]);
pgStatCollectorPmPipe[0] = -1;
if (pgStatCollectorPmPipe[1] >= 0)
closesocket(pgStatCollectorPmPipe[1]);
pgStatCollectorPmPipe[1] = -1;
}
/* ----------
* pgstat_beterm() -
*
@@ -1445,15 +1401,6 @@ PgstatBufferMain(int argc, char *argv[])
pgstat_parseArgs(argc,argv);
#endif
/*
* Close the writing end of the postmaster pipe, so we'll see it
* closing when the postmaster terminates and can terminate as well.
*/
closesocket(pgStatPmPipe[1]);
pgStatPmPipe[1] = -1;
closesocket(pgStatCollectorPmPipe[1]);
pgStatCollectorPmPipe[1] = -1;
/*
* Start a buffering process to read from the socket, so we have a
* little more time to process incoming messages.
@@ -1517,7 +1464,6 @@ PgstatCollectorMain(int argc, char *argv[])
PgStat_Msg msg;
fd_set rfds;
int readPipe;
int pmPipe;
int nready;
int len = 0;
struct timeval timeout;
@@ -1555,7 +1501,6 @@ PgstatCollectorMain(int argc, char *argv[])
/* Close unwanted files */
closesocket(pgStatPipe[1]);
closesocket(pgStatSock);
pmPipe = pgStatCollectorPmPipe[0];
/*
* Identify myself via ps
@@ -1823,17 +1768,9 @@ PgstatCollectorMain(int argc, char *argv[])
* shutdown, we want to save the final stats to reuse at next startup.
* But if the buffer process failed, it seems best not to (there may
* even now be a new collector firing up, and we don't want it to read
* a partially- rewritten stats file). We can tell whether the
* postmaster is still alive by checking to see if the postmaster pipe
* is still open. If it is read-ready (ie, EOF), the postmaster must
* have quit.
* a partially-rewritten stats file).
*/
FD_ZERO(&rfds);
FD_SET(pmPipe, &rfds);
timeout.tv_sec = 0;
timeout.tv_usec = 0;
nready = select(pmPipe+1,&rfds,NULL,NULL,&timeout);
if (nready > 0 && FD_ISSET(pmPipe, &rfds))
if (!PostmasterIsAlive(false))
pgstat_write_statsfile();
}
@@ -1852,8 +1789,8 @@ pgstat_recvbuffer(void)
{
fd_set rfds;
fd_set wfds;
struct timeval timeout;
int writePipe = pgStatPipe[1];
int pmPipe = pgStatPmPipe[0];
int maxfd;
int nready;
int len;
@@ -1937,8 +1874,7 @@ pgstat_recvbuffer(void)
/*
* If we have messages to write out, we add the pipe to the write
* descriptor set. Otherwise, we check if the postmaster might
* have terminated.
* descriptor set.
*/
if (msg_have > 0)
{
@@ -1946,17 +1882,16 @@ pgstat_recvbuffer(void)
if (writePipe > maxfd)
maxfd = writePipe;
}
else
{
FD_SET(pmPipe, &rfds);
if (pmPipe > maxfd)
maxfd = pmPipe;
}
/*
* Wait for some work to do.
* Wait for some work to do; but not for more than 10 seconds
* (this determines how quickly we will shut down after postmaster
* termination).
*/
nready = select(maxfd + 1, &rfds, &wfds, NULL, NULL);
timeout.tv_sec = 10;
timeout.tv_usec = 0;
nready = select(maxfd + 1, &rfds, &wfds, NULL, &timeout);
if (nready < 0)
{
if (errno == EINTR)
@@ -2062,11 +1997,9 @@ pgstat_recvbuffer(void)
continue;
/*
* If the pipe from the postmaster is ready for reading, the
* kernel must have closed it on exit() (the postmaster never
* really writes to it). So we've done our job.
* If the postmaster has terminated, we've done our job.
*/
if (FD_ISSET(pmPipe, &rfds))
if (!PostmasterIsAlive(true))
exit(0);
}
}

File diff suppressed because it is too large Load Diff