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 */

}

猜你喜欢

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