Postgresql - 原始碼 - backend啟動
如何調到這個postmaster子程序呢,後臺程式
postmaster backend子程序存在在backend/main/main.c
1. 進入到src/backend/main/main.c中,檢視後臺主函式main
/*
* ......
* This does some essential startup tasks for any incarnation of postgres
* (postmaster, standalone backend, standalone bootstrap process, or a
* separately exec'd child of a postmaster) and then dispatches to the
* proper FooMain() routine for the incarnation.
* ......
*/
/*
* Any Postgres server process begins execution here.
*/
int
main(int argc, char *argv[])
{
......
progname = get_progname(argv[0]);
startup_hacks(progname);
argv = save_ps_display_args(argc, argv);
MemoryContextInit();
set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("postgres"));
......
if (argc > 1)
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
help(progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
{
fputs(PG_BACKEND_VERSIONSTR, stdout);
exit(0);
}
if (strcmp(argv[1], "--describe-config") == 0)
do_check_root = false;
else if (argc > 2 && strcmp(argv[1], "-C") == 0)
do_check_root = false;
}
if (do_check_root)
check_root(progname);
......
if (argc > 1 && strcmp(argv[1], "--boot") == 0)
AuxiliaryProcessMain(argc, argv); /* does not return */
else if (argc > 1 && strcmp(argv[1], "--describe-config") == 0)
GucInfoMain(); /* does not return */
/* single 方式啟動*/
else if (argc > 1 && strcmp(argv[1], "--single") == 0)
PostgresMain(argc, argv,
NULL, /* no dbname */
strdup(get_user_name_or_exit(progname))); /* does not return */
else
/* 正常啟動 */
PostmasterMain(argc, argv); /* does not return */
abort(); /* should not get here */
}
2. 我們看一下正常啟動是如何操作的,都經歷哪些步驟,src/backend/postmaster/postmaster.c 中的PostmasterMain()函式。
/*
* Postmaster main entry point postmaster 程序主入口點,啟動之後就會開啟多個程序,都是從這裡進去的。
*/
void
PostmasterMain(int argc, char *argv[])
{
int opt;
int status;
char *userDoption = NULL;
bool listen_addr_saved = false;
int i;
char *output_config_variable = NULL;
/* 獲取PID */
MyProcPid = PostmasterPid = getpid();
/* 啟動時間 */
MyStartTime = time(NULL);
IsPostmasterEnvironment = true;
/*
* We should not be creating any files or directories before we check the
* data directory (see checkDataDir()), but just in case set the umask to
* the most restrictive (owner-only) permissions.
*
* checkDataDir() will reset the umask based on the data directory
* permissions.
*/
/* 許可權 */
umask(PG_MODE_MASK_OWNER);
/*
* Initialize random(3) so we don't get the same values in every run.
*
* Note: the seed is pretty predictable from externally-visible facts such
* as postmaster start time, so avoid using random() for security-critical
* random values during postmaster startup. At the time of first
* connection, PostmasterRandom will select a hopefully-more-random seed.
*/
/* */
srandom((unsigned int) (MyProcPid ^ MyStartTime));
/*
* By default, palloc() requests in the postmaster will be allocated in
* the PostmasterContext, which is space that can be recycled by backends.
* Allocated data that needs to be available to backends should be
* allocated in TopMemoryContext.
*/
/* 分配執行空間 */
PostmasterContext = AllocSetContextCreate(TopMemoryContext,
"Postmaster",
ALLOCSET_DEFAULT_SIZES);
MemoryContextSwitchTo(PostmasterContext);
/* Initialize paths to installation files */ /* 初始化安裝檔案的路徑 */
getInstallationPaths(argv[0]);
/*
* Set up signal handlers for the postmaster process.
*
* In the postmaster, we want to install non-ignored handlers *without*
* SA_RESTART. This is because they'll be blocked at all times except
* when ServerLoop is waiting for something to happen, and during that
* window, we want signals to exit the select(2) wait so that ServerLoop
* can respond if anything interesting happened. On some platforms,
* signals marked SA_RESTART would not cause the select() wait to end.
* Child processes will generally want SA_RESTART, but we expect them to
* set up their own handlers before unblocking signals.
*
* CAUTION: when changing this list, check for side-effects on the signal
* handling setup of child processes. See tcop/postgres.c,
* bootstrap/bootstrap.c, postmaster/bgwriter.c, postmaster/walwriter.c,
* postmaster/autovacuum.c, postmaster/pgarch.c, postmaster/pgstat.c,
* postmaster/syslogger.c, postmaster/bgworker.c and
* postmaster/checkpointer.c.
*/
/* 為postmaster程序設定訊號處理程式。 */
pqinitmask();
PG_SETMASK(&BlockSig);
pqsignal_no_restart(SIGHUP, SIGHUP_handler); /* reread config file and have children do same */
......
pqsignal(SIGTTOU, SIG_IGN); /* ignored */
/* ignore SIGXFSZ, so that ulimit violations work like disk full */
#ifdef SIGXFSZ
pqsignal(SIGXFSZ, SIG_IGN); /* ignored */
#endif
/*
* Options setup 選擇引數設定
*/
InitializeGUCOptions();
opterr = 1;
/*
* Parse command-line options. CAUTION: keep this in sync with
* tcop/postgres.c (the option sets should not conflict) and with the
* common help() function in main/main.c.
*/
/* 解析命令列啟動引數 */
while ((opt = getopt(argc, argv, "B:bc:C:D:d:EeFf:h:ijk:lN:nOo:Pp:r:S:sTt:W:-:")) != -1)
{
switch (opt)
{
case ... case ... case ... ......
}
}
/*
* Postmaster accepts no non-option switch arguments.
*/
/* Postmaster不接受非選項開關引數。 */
if (optind < argc)
{
write_stderr("%s: invalid argument: \"%s\"\n",
progname, argv[optind]);
write_stderr("Try \"%s --help\" for more information.\n",
progname);
ExitPostmaster(1);
}
/*
* Locate the proper configuration files and data directory, and read
* postgresql.conf for the first time.
*/
/* 找到正確的配置檔案和資料目錄,第一次讀取postgresql.conf。 */
if (!SelectConfigFiles(userDoption, progname))
ExitPostmaster(2);
if (output_config_variable != NULL)
{
/*
* "-C guc" was specified, so print GUC's value and exit. No extra
* permission check is needed because the user is reading inside the
* data dir.
*/
const char *config_val = GetConfigOption(output_config_variable,
false, false);
puts(config_val ? config_val : "");
ExitPostmaster(0);
}
/* Verify that DataDir looks reasonable */
/* 驗證DataDir */
checkDataDir();
/* Check that pg_control exists */
/* 驗證pg_control是否存在 */
checkControlFile();
/* And switch working directory into it */
/* 將工作目錄更改為DataDir。大多數postmaster和後端程式碼都假設我們在DataDir中,
* 因此它可以使用相對路徑訪問資料目錄中的內容。但是,為了在路徑設定過程中方便起見,
* 我們不強制在SetDataDir期間執行chdir。
*/
ChangeToDataDir();
/*
* Check for invalid combinations of GUC settings.
*/
/* 檢查GUC設定的無效組合。包括程序、連線,XLog、wal_level等 */
if (ReservedBackends + max_wal_senders >= MaxConnections)
......
if (XLogArchiveMode > ARCHIVE_MODE_OFF && wal_level == WAL_LEVEL_MINIMAL)
......
if (max_wal_senders > 0 && wal_level == WAL_LEVEL_MINIMAL)
......
/*
* Other one-time internal sanity checks can go here, if they are fast.
* (Put any slow processing further down, after postmaster.pid creation.)
*/
/* 其他一次性的內部完整性檢查可以在這裡進行。 */
if (!CheckDateTokenTables())
......
/*
* Now that we are done processing the postmaster arguments, reset
* getopt(3) library so that it will work correctly in subprocesses.
*/
/* 現在我們已經完成了postmaster引數的處理,重新設定getopt(3)庫,以便它能夠在子程序中正確工作。 */
optind = 1;
#ifdef HAVE_INT_OPTRESET
optreset = 1; /* some systems need this too */
#endif
/* For debugging: display postmaster environment */
/* 當開啟DEBUG記錄日誌,可以從日誌中看到詳細記錄(迴圈展示的引數) */
{
......
}
/*
* Create lockfile for data directory.
*
* We want to do this before we try to grab the input sockets, because the
* data directory interlock is more reliable than the socket-file
* interlock (thanks to whoever decided to put socket files in /tmp :-().
* For the same reason, it's best to grab the TCP socket(s) before the
* Unix socket(s).
*
* Also note that this internally sets up the on_proc_exit function that
* is responsible for removing both data directory and socket lockfiles;
* so it must happen before opening sockets so that at exit, the socket
* lockfiles go away after CloseServerPorts runs.
*/
/* 為資料目錄建立鎖檔案。
* 在嘗試獲取輸入套接字之前,我們需要這樣做,因為資料目錄聯鎖比套接字-檔案聯鎖更可靠
* (這要感謝決定將套接字檔案放在/tmp:-()中的人)。出於同樣的原因,最好在Unix套接字之前獲取TCP套接字。
* 還要注意,這在內部設定了on_proc_exit函式,該函式負責刪除資料目錄和套接字鎖檔案;
* 因此,它必須在開啟套接字之前發生,以便在退出時,套接字鎖檔案在CloseServerPorts執行之後消失。
*/
CreateDataDirLockFile(true);
/*
* Read the control file (for error checking and config info).
*
* Since we verify the control file's CRC, this has a useful side effect
* on machines where we need a run-time test for CRC support instructions.
* The postmaster will do the test once at startup, and then its child
* processes will inherit the correct function pointer and not need to
* repeat the test.
*/
/* 讀取控制檔案(用於錯誤檢查和配置資訊)。因為我們驗證了控制檔案的CRC,
* 所以這對需要對CRC支援指令進行執行時測試的機器有一個有用的副作用。
* postmaster將在啟動時執行一次測試,然後它的子程序將繼承正確的函式指標,不需要重複測試。
*/
LocalProcessControlFile(false);
/*
* Initialize SSL library, if specified.
*/
/* 初始化SSL lib */
#ifdef USE_SSL
if (EnableSSL)
{
(void) secure_initialize(true);
LoadedSSL = true;
}
#endif
/*
* Register the apply launcher. Since it registers a background worker,
* it needs to be called before InitializeMaxBackends(), and it's probably
* a good idea to call it before any modules had chance to take the
* background worker slots.
*/
/* 註冊應用啟動程式。因為它註冊了一個後臺工作者,所以它需要在InitializeMaxBackends()之前被呼叫,
* 在任何模組有機會獲得後臺工作者插槽之前呼叫它可能是個好主意。
*/
ApplyLauncherRegister();
/*
* process any libraries that should be preloaded at postmaster start
*/
process_shared_preload_libraries();
/*
* Now that loadable modules have had their chance to register background
* workers, calculate MaxBackends.
*/
InitializeMaxBackends();
/*
* Establish input sockets.
*
* First, mark them all closed, and set up an on_proc_exit function that's
* charged with closing the sockets again at postmaster shutdown.
*/
/* 建立各類輸入sockets,首先,標記它們都已關閉,並設定on_proc_exit函式,
* 該函式負責在postmaster shutdown時再次關閉套接字。
* 監聽地址,等
*/
for (i = 0; i < MAXLISTEN; i++)
ListenSocket[i] = PGINVALID_SOCKET;
on_proc_exit(CloseServerPorts, 0);
/* 對每一個監聽地址進行操作 */
if (ListenAddresses)
{
......
}
#ifdef USE_BONJOUR
/* Register for Bonjour only if we opened TCP socket(s) */
/* 只有當我們開啟TCP套接字(s)時,才能註冊Bonjour */
......
#endif
#ifdef HAVE_UNIX_SOCKETS
/* 如果定義了HAVE_UNIX_SOCKETS,執行 */
if (Unix_socket_directories)
......
#endif
/*
* check that we have some socket to listen on
* 檢查一下我們是否有監聽器
*/
if (ListenSocket[0] == PGINVALID_SOCKET)
ereport(FATAL,
(errmsg("no socket created for listening")));
/*
* If no valid TCP ports, write an empty line for listen address,
* indicating the Unix socket must be used. Note that this line is not
* added to the lock file until there is a socket backing it.
*/
/* 如果沒有有效的TCP埠,則為偵聽地址編寫空行,指示必須使用Unix套接字。
* 注意,除非有套接字支援,否則不會將這一行新增到鎖檔案中。 */
if (!listen_addr_saved)
AddToDataDirLockFile(LOCK_FILE_LINE_LISTEN_ADDR, "");
/*
* Set up shared memory and semaphores.
*/
/* 設定共享記憶體和訊號量。 */
reset_shared(PostPortNumber);
/*
* Estimate number of openable files. This must happen after setting up
* semaphores, because on some platforms semaphores count as open files.
*/
/* 估計可開啟檔案的數量。這必須在設定訊號量之後發生,因為在某些平臺上訊號量算作開啟的檔案。 */
set_max_safe_fds();
/*
* Set reference point for stack-depth checking.
*/
/* 設定堆疊深度檢查的參考點。 */
set_stack_base();
/*
* Initialize pipe (or process handle on Windows) that allows children to
* wake up from sleep on postmaster death.
*/
/* 初始化管道(或Windows上的程序控制代碼),允許postmaster的子程序醒來。 */
InitPostmasterDeathWatchHandle();
#ifdef WIN32
/*
* Initialize I/O completion port used to deliver list of dead children.
*/
/* 初始化I/O完成埠 */
win32ChildQueue = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1);
if (win32ChildQueue == NULL)
ereport(FATAL,
(errmsg("could not create I/O completion port for child queue")));
#endif
/*
* Record postmaster options. We delay this till now to avoid recording
* bogus options (eg, NBuffers too high for available memory).
*/
/* 記錄postmaster的選項。我們將此延遲到現在,以避免記錄偽造的選項 */
if (!CreateOptsFile(argc, argv, my_exec_path))
ExitPostmaster(1);
#ifdef EXEC_BACKEND
/* Write out nondefault GUC settings for child processes to use */
write_nondefault_variables(PGC_POSTMASTER);
#endif
/*
* Write the external PID file if requested
*/
if (external_pid_file)
{
......
}
/*
* Remove old temporary files. At this point there can be no other
* Postgres processes running in this directory, so this should be safe.
*/
/* 刪除舊的temporary files */
RemovePgTempFiles();
/*
* Forcibly remove the files signaling a standby promotion request.
* Otherwise, the existence of those files triggers a promotion too early,
* whether a user wants that or not.
*
* This removal of files is usually unnecessary because they can exist
* only during a few moments during a standby promotion. However there is
* a race condition: if pg_ctl promote is executed and creates the files
* during a promotion, the files can stay around even after the server is
* brought up to new master. Then, if new standby starts by using the
* backup taken from that master, the files can exist at the server
* startup and should be removed in order to avoid an unexpected
* promotion.
*
* Note that promotion signal files need to be removed before the startup
* process is invoked. Because, after that, they can be used by
* postmaster's SIGUSR1 signal handler.
*/
/* 強制刪除傳送備用升級請求的檔案。否則,無論使用者是否希望這樣做,這些檔案的存在都會過早地觸發升級。
* 刪除檔案通常是不必要的,因為它們只能在備用升級過程中的一小段時間記憶體在。
* 但是這裡有一個競態條件:如果執行pg_ctl提升並在提升過程中建立檔案,那麼即使伺服器被提升到新的主伺服器上,
* 這些檔案也可以保持不變。然後,如果使用從主伺服器獲取的備份啟動新的備用伺服器,
* 那麼這些檔案可以在伺服器啟動時存在,應該刪除這些檔案,以避免意外升級。
* 注意,在呼叫啟動程序之前,需要刪除升級訊號檔案。因為,在那之後,它們可以被postmaster的SIGUSR1訊號處理器使用。
*/
RemovePromoteSignalFiles();
/* Remove any outdated file holding the current log filenames. */
/* 刪除任何儲存當前日誌檔名的過時檔案。 */
if (unlink(LOG_METAINFO_DATAFILE) < 0 && errno != ENOENT)
ereport(LOG,
(errcode_for_file_access(),
errmsg("could not remove file \"%s\": %m",
LOG_METAINFO_DATAFILE)));
/*
* If enabled, start up syslogger collection subprocess
*/
SysLoggerPID = SysLogger_Start();
/*
* Reset whereToSendOutput from DestDebug (its starting state) to
* DestNone. This stops ereport from sending log messages to stderr unless
* Log_destination permits. We don't do this until the postmaster is
* fully launched, since startup failures may as well be reported to
* stderr.
*
* If we are in fact disabling logging to stderr, first emit a log message
* saying so, to provide a breadcrumb trail for users who may not remember
* that their logging is configured to go somewhere else.
*/
/* 重置whereToSendOutput from DestDebug(它的起始狀態)到DestNone。
* 這將阻止ereport向stderr傳送日誌訊息,除非Log_destination允許。
* 如果我們實際上要禁用stderr日誌記錄,那麼首先發出一條這樣說的日誌訊息,
* 為那些可能不記得日誌記錄被配置到其他地方的使用者提供一個breadcrumb跟蹤。 */
if (!(Log_destination & LOG_DESTINATION_STDERR))
ereport(LOG,
(errmsg("ending log output to stderr"),
errhint("Future log output will go to log destination \"%s\".",
Log_destination_string)));
whereToSendOutput = DestNone;
/*
* Initialize stats collection subsystem (this does NOT start the
* collector process!)
*/
/* 初始化stats collection子系統(這不會啟動收集器程序!) */
pgstat_init();
/*
* Initialize the autovacuum subsystem (again, no process start yet)
*/
/* 初始化自動vacuum子系統 */
autovac_init();
/*
* Load configuration files for client authentication.
*/
/* 載入配置檔案 hba 和 ident */
if (!load_hba())
......
if (!load_ident())
......
#ifdef HAVE_PTHREAD_IS_THREADED_NP
......
#endif
/*
* Remember postmaster startup time
*/
PgStartTime = GetCurrentTimestamp();
#ifndef HAVE_STRONG_RANDOM
/* RandomCancelKey wants its own copy */
gettimeofday(&random_start_time, NULL);
#endif
/*
* Report postmaster status in the postmaster.pid file, to allow pg_ctl to
* see what's happening.
*/
/* 在postmaster中報告postmaster狀態。pid檔案,允許pg_ctl看到發生了什麼。 */
AddToDataDirLockFile(LOCK_FILE_LINE_PM_STATUS, PM_STATUS_STARTING);
/*
* We're ready to rock and roll...
*/
StartupPID = StartupDataBase();
Assert(StartupPID != 0);
StartupStatus = STARTUP_RUNNING;
pmState = PM_STARTUP;
/* Some workers may be scheduled to start now */
/* 一些workers 現在開始工作 */
maybe_start_bgworkers();
status = ServerLoop();
/*
* ServerLoop probably shouldn't ever return, but if it does, close down.
*/
ExitPostmaster(status != STATUS_OK);
abort(); /* not reached */
}