Postgresql - 源码 - WalWriter process

启动时执行两个函数

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);

    }

}

猜你喜欢

转载自blog.csdn.net/chuckchen1222/article/details/82942339