启动时执行两个函数
InitXLOGAccess(); 代码位置 src/backend/access/transam/xlog.c
WalWriterMain(); 代码位置 src/backend/postmaster/walwriter.c
InitXLOGAccess()的解释是:在创建WAR记录之前,必须在后端过程中调用。我们需要初始化ThisTimeLineID 和 RedoRecPtr 的本地副本。
代码不长,是对LOG访问的初始化。
void InitXLOGAccess(void)
{
XLogCtlInsert *Insert = &XLogCtl->Insert;
/* ThisTimeLineID doesn't change so we need no lock to copy it */
ThisTimeLineID = XLogCtl->ThisTimeLineID;
Assert(ThisTimeLineID != 0 || IsBootstrapProcessingMode());
/* set wal_segment_size */
wal_segment_size = ControlFile->xlog_seg_size;
/* Use GetRedoRecPtr to copy the RedoRecPtr safely */
(void) GetRedoRecPtr();
/* Also update our copy of doPageWrites. */
doPageWrites = (Insert->fullPageWrites || Insert->forcePageWrites);
/* Also initialize the working areas for constructing WAL records */
InitXLogInsert();
}
对于WalWriter process,主入口函数是WalWriterMain(void),我们来看一下它的代码。
void
WalWriterMain(void)
{
sigjmp_buf local_sigjmp_buf;
MemoryContext walwriter_context;
int left_till_hibernate;
bool hibernating;
/* 正确接受或忽略 postmaster 可能发送给我们的信号 */
pqsignal(SIGHUP, WalSigHupHandler); /* set flag to read config file */
pqsignal(SIGINT, WalShutdownHandler); /* request shutdown */
pqsignal(SIGTERM, WalShutdownHandler); /* request shutdown */
pqsignal(SIGQUIT, wal_quickdie); /* hard crash time */
pqsignal(SIGALRM, SIG_IGN);
pqsignal(SIGPIPE, SIG_IGN);
pqsignal(SIGUSR1, walwriter_sigusr1_handler);
pqsignal(SIGUSR2, SIG_IGN); /* not used */
/*
* 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 */
sigdelset(&BlockSig, SIGQUIT);
/* 创建一个 resource owner 来跟踪我们的资源 */
CurrentResourceOwner = ResourceOwnerCreate(NULL, "Wal Writer");
/* 创建一个内存上下文,我们将完成所有的工作。我们这样做,以便在错误恢复过程中可以重置上下文,从而避免可能的内存泄漏。以前,这段代码只是在TopMemoryContext运行,但是重新设置这将是一个非常糟糕的想法。 */
walwriter_context = AllocSetContextCreate(TopMemoryContext,
"Wal Writer",
ALLOCSET_DEFAULT_SIZES);
MemoryContextSwitchTo(walwriter_context);
/* 如果遇到异常,则在此恢复处理。 */
if (sigsetjmp(local_sigjmp_buf, 1) != 0)
{
/* 由于不使用 PG_TRY,必须手动重置错误堆栈。 */
error_context_stack = NULL;
/* 清理时防止中断 */
HOLD_INTERRUPTS();
/* Report the error to the server log */
EmitErrorReport();
/* 这些操作实际上只是AbortTransaction() 的最小子集。我们在walwriter没有太多的资源需要担心,但我们确实有LWLocks ,也许还有缓冲区?*/
LWLockReleaseAll();
ConditionVariableCancelSleep();
pgstat_report_wait_end();
AbortBufferIO();
UnlockBuffers();
/* buffer pins are released here: */
ResourceOwnerRelease(CurrentResourceOwner,
RESOURCE_RELEASE_BEFORE_LOCKS,
false, true);
/* 我们不必担心其他 ResourceOwnerRelease 阶段。 */
AtEOXact_Buffers(false);
AtEOXact_SMgr();
AtEOXact_Files(false);
AtEOXact_HashTables(false);
/* 现在返回正常的顶级上下文,下次清除错误上下文。*/
MemoryContextSwitchTo(walwriter_context);
FlushErrorState();
/* 在顶层上下文中清除任何泄漏的数据 */
MemoryContextResetAndDeleteChildren(walwriter_context);
/* 现在我们可以再次中断 */
RESUME_INTERRUPTS();
/* 在任何错误之后睡眠至少1秒。一个写错误很可能会被重复,并且我们不想以尽可能快的速度填充错误日志。*/
pg_usleep(1000000L);
/* 在所有错误之后关闭所有打开的文件。这在Windows上是有用的,在那里保存删除的文件会导致各种奇怪的错误。目前还不清楚我们需要在别处,但不应该伤害。 */
smgrcloseall();
}
/* We can now handle ereport(ERROR) */
PG_exception_stack = &local_sigjmp_buf;
/*
* Unblock signals (they were blocked when the postmaster forked us)
*/
PG_SETMASK(&UnBlockSig);
/*
* Reset hibernation state after any error.
*/
left_till_hibernate = LOOPS_UNTIL_HIBERNATE;
hibernating = false;
SetWalWriterSleeping(false);
/* 告诉闩锁,当我们睡觉的时候后台可以随时叫醒我们。 */
ProcGlobal->walwriterLatch = &MyProc->procLatch;
/* 循环 */
for (;;)
{
long cur_timeout;
int rc;
/* 通知我们是否可以在这个周期休眠。在重置锁存器之前这样做,以确保如果任何异步提交可能需要唤醒我们,它们将看到标志集,并且我们不会错过它们发送给我们的任何信号。(如果我们在休眠之前的最后一个周期中发现需要做的工作,那么全局标志将被不必要地设置,但是伤害很小。)但是如果不需要更改,则避免触摸全局标志。 */
if (hibernating != (left_till_hibernate <= 1))
{
hibernating = (left_till_hibernate <= 1);
SetWalWriterSleeping(hibernating);
}
/* 清除任何已挂起的唤醒 */
ResetLatch(MyLatch);
/* 处理最近收到的任何请求或信号 */
if (got_SIGHUP)
{
got_SIGHUP = false;
ProcessConfigFile(PGC_SIGHUP);
}
if (shutdown_requested)
{
/* Normal exit from the walwriter is here */
proc_exit(0); /* done */
}
/* 做我们在这里做的事情;然后,如果XLogBackgroundFlush() 找到有用的工作要做,请重新设置休眠计数器。 */
if (XLogBackgroundFlush())
left_till_hibernate = LOOPS_UNTIL_HIBERNATE;
else if (left_till_hibernate > 0)
left_till_hibernate--;
/* 休眠直到我们发出信号或 WalWriterDelay 已经过去。如果我们已经相当长一段时间没有做任何有用的事情了,延长休眠时间以减少服务器的空闲功耗。 */
if (left_till_hibernate > 0)
cur_timeout = WalWriterDelay; /* in ms */
else
cur_timeout = WalWriterDelay * HIBERNATE_FACTOR;
rc = WaitLatch(MyLatch,
WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
cur_timeout,
WAIT_EVENT_WAL_WRITER_MAIN);
/* 如果postmaster 进程死了,将紧急救助。这是为了避免对所有postmaster的子进程进行手工清理。 */
if (rc & WL_POSTMASTER_DEATH)
exit(1);
}
}