1. 程式人生 > >Postgresql - 原始碼 - backend啟動

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

}