如何调到这个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 */
}