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