千家信息网

PostgreSQL的备份工具pg_basebackup源码中的主函数分析

发表于:2025-11-14 作者:千家信息网编辑
千家信息网最后更新 2025年11月14日,本篇内容主要讲解"PostgreSQL的备份工具pg_basebackup源码中的主函数分析",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"PostgreS
千家信息网最后更新 2025年11月14日PostgreSQL的备份工具pg_basebackup源码中的主函数分析

本篇内容主要讲解"PostgreSQL的备份工具pg_basebackup源码中的主函数分析",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"PostgreSQL的备份工具pg_basebackup源码中的主函数分析"吧!

一、数据结构

option
使用工具时存储选项的数据结构

#ifndef HAVE_STRUCT_OPTION//工具软件选项struct option{    const char *name;//名称    int         has_arg;//是否包含参数,no_argument/required_argument/optional_argument    int        *flag;//标记    int         val;//参数值};#define no_argument 0#define required_argument 1#define optional_argument 2#endif/* * On OpenBSD and some versions of Solaris, opterr and friends are defined in * core libc rather than in a separate getopt module.  Define these variables * only if configure found they aren't there by default; otherwise, this * module and its callers will just use libc's variables.  (We assume that * testing opterr is sufficient for all of these.) */#ifndef HAVE_INT_OPTERRint         opterr = 1,         /* if error message should be printed */            optind = 1,         /* index into parent argv vector */            optopt;             /* character checked for validity */char       *optarg;             /* argument associated with option */#endif#define BADCH   (int)'?'#define BADARG  (int)':'#define EMSG    ""

二、源码解读

pg_basebackup主函数,源码较为简单,获取选项,校验,调用BaseBackup()函数进行备份.

intmain(int argc, char **argv){    static struct option long_options[] = {        {"help", no_argument, NULL, '?'},        {"version", no_argument, NULL, 'V'},        {"pgdata", required_argument, NULL, 'D'},        {"format", required_argument, NULL, 'F'},        {"checkpoint", required_argument, NULL, 'c'},        {"create-slot", no_argument, NULL, 'C'},        {"max-rate", required_argument, NULL, 'r'},        {"write-recovery-conf", no_argument, NULL, 'R'},        {"slot", required_argument, NULL, 'S'},        {"tablespace-mapping", required_argument, NULL, 'T'},        {"wal-method", required_argument, NULL, 'X'},        {"gzip", no_argument, NULL, 'z'},        {"compress", required_argument, NULL, 'Z'},        {"label", required_argument, NULL, 'l'},        {"no-clean", no_argument, NULL, 'n'},        {"no-sync", no_argument, NULL, 'N'},        {"dbname", required_argument, NULL, 'd'},        {"host", required_argument, NULL, 'h'},        {"port", required_argument, NULL, 'p'},        {"username", required_argument, NULL, 'U'},        {"no-password", no_argument, NULL, 'w'},        {"password", no_argument, NULL, 'W'},        {"status-interval", required_argument, NULL, 's'},        {"verbose", no_argument, NULL, 'v'},        {"progress", no_argument, NULL, 'P'},        {"waldir", required_argument, NULL, 1},        {"no-slot", no_argument, NULL, 2},        {"no-verify-checksums", no_argument, NULL, 3},        {NULL, 0, NULL, 0}    };//(完整)选项    //选项的ASCII值    int         c;    //选项索引编号    int         option_index;    //程序名称    progname = get_progname(argv[0]);    set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_basebackup"));    if (argc > 1)    {        //显示帮助信息        if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)        {            usage();            exit(0);        }        else if (strcmp(argv[1], "-V") == 0                 || strcmp(argv[1], "--version") == 0)        {            //显示版本信息            puts("pg_basebackup (PostgreSQL) " PG_VERSION);            exit(0);        }    }    atexit(cleanup_directories_atexit);    //getopt_long --> 获取选项    while ((c = getopt_long(argc, argv, "CD:F:r:RS:T:X:l:nNzZ:d:c:h:p:U:s:wWkvP",                            long_options, &option_index)) != -1)    {        switch (c)//根据选项设置参数        {            case 'C':                create_slot = true;                break;            case 'D':                basedir = pg_strdup(optarg);                break;            case 'F':                if (strcmp(optarg, "p") == 0 || strcmp(optarg, "plain") == 0)                    format = 'p';//不压缩                else if (strcmp(optarg, "t") == 0 || strcmp(optarg, "tar") == 0)                    format = 't';//tar包                else                {                    fprintf(stderr,                            _("%s: invalid output format \"%s\", must be \"plain\" or \"tar\"\n"),                            progname, optarg);                    exit(1);                }                break;            case 'r':                maxrate = parse_max_rate(optarg);                break;            case 'R':                writerecoveryconf = true;                break;            case 'S':                /*                 * When specifying replication slot name, use a permanent                 * slot.                 * 指定复制槽名称,则使用持久化槽                 */                replication_slot = pg_strdup(optarg);                temp_replication_slot = false;                break;            case 2:                no_slot = true;                break;            case 'T':                tablespace_list_append(optarg);                break;            case 'X':                if (strcmp(optarg, "n") == 0 ||                    strcmp(optarg, "none") == 0)                {                    includewal = NO_WAL;                }                else if (strcmp(optarg, "f") == 0 ||                         strcmp(optarg, "fetch") == 0)                {                    includewal = FETCH_WAL;                }                else if (strcmp(optarg, "s") == 0 ||                         strcmp(optarg, "stream") == 0)                {                    includewal = STREAM_WAL;                }                else                {                    fprintf(stderr,                            _("%s: invalid wal-method option \"%s\", must be \"fetch\", \"stream\", or \"none\"\n"),                            progname, optarg);                    exit(1);                }                break;            case 1:                xlog_dir = pg_strdup(optarg);                break;            case 'l':                label = pg_strdup(optarg);                break;            case 'n':                noclean = true;                break;            case 'N':                do_sync = false;                break;            case 'z':#ifdef HAVE_LIBZ                compresslevel = Z_DEFAULT_COMPRESSION;#else                compresslevel = 1;  /* will be rejected below */#endif                break;            case 'Z':                compresslevel = atoi(optarg);                if (compresslevel < 0 || compresslevel > 9)                {                    fprintf(stderr, _("%s: invalid compression level \"%s\"\n"),                            progname, optarg);                    exit(1);                }                break;            case 'c':                if (pg_strcasecmp(optarg, "fast") == 0)                    fastcheckpoint = true;                else if (pg_strcasecmp(optarg, "spread") == 0)                    fastcheckpoint = false;                else                {                    fprintf(stderr, _("%s: invalid checkpoint argument \"%s\", must be \"fast\" or \"spread\"\n"),                            progname, optarg);                    exit(1);                }                break;            case 'd':                connection_string = pg_strdup(optarg);                break;            case 'h':                dbhost = pg_strdup(optarg);                break;            case 'p':                dbport = pg_strdup(optarg);                break;            case 'U':                dbuser = pg_strdup(optarg);                break;            case 'w':                dbgetpassword = -1;                break;            case 'W':                dbgetpassword = 1;                break;            case 's':                standby_message_timeout = atoi(optarg) * 1000;                if (standby_message_timeout < 0)                {                    fprintf(stderr, _("%s: invalid status interval \"%s\"\n"),                            progname, optarg);                    exit(1);                }                break;            case 'v':                verbose++;                break;            case 'P':                showprogress = true;                break;            case 3:                verify_checksums = false;                break;            default:                /*                 * getopt_long already emitted a complaint                 * 非法参数                 */                fprintf(stderr, _("Try \"%s --help\" for more information.\n"),                        progname);                exit(1);        }    }    /*     * Any non-option arguments?     * 不识别的参数     */    if (optind < argc)    {        fprintf(stderr,                _("%s: too many command-line arguments (first is \"%s\")\n"),                progname, argv[optind]);        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),                progname);        exit(1);    }    /*     * Required arguments     * 必须的参数     */    if (basedir == NULL)    {        fprintf(stderr, _("%s: no target directory specified\n"), progname);        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),                progname);        exit(1);    }    /*     * Mutually exclusive arguments     * 互斥的参数     */    if (format == 'p' && compresslevel != 0)    {        fprintf(stderr,                _("%s: only tar mode backups can be compressed\n"),                progname);        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),                progname);        exit(1);    }    if (format == 't' && includewal == STREAM_WAL && strcmp(basedir, "-") == 0)    {        fprintf(stderr,                _("%s: cannot stream write-ahead logs in tar mode to stdout\n"),                progname);        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),                progname);        exit(1);    }    if (replication_slot && includewal != STREAM_WAL)    {        fprintf(stderr,                _("%s: replication slots can only be used with WAL streaming\n"),                progname);        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),                progname);        exit(1);    }    if (no_slot)    {        if (replication_slot)        {            fprintf(stderr,                    _("%s: --no-slot cannot be used with slot name\n"),                    progname);            fprintf(stderr, _("Try \"%s --help\" for more information.\n"),                    progname);            exit(1);        }        temp_replication_slot = false;    }    if (create_slot)    {        if (!replication_slot)        {            fprintf(stderr,                    _("%s: %s needs a slot to be specified using --slot\n"),                    progname, "--create-slot");            fprintf(stderr, _("Try \"%s --help\" for more information.\n"),                    progname);            exit(1);        }        if (no_slot)        {            fprintf(stderr,                    _("%s: --create-slot and --no-slot are incompatible options\n"),                    progname);            fprintf(stderr, _("Try \"%s --help\" for more information.\n"),                    progname);            exit(1);        }    }    if (xlog_dir)    {        if (format != 'p')        {            fprintf(stderr,                    _("%s: WAL directory location can only be specified in plain mode\n"),                    progname);            fprintf(stderr, _("Try \"%s --help\" for more information.\n"),                    progname);            exit(1);        }        /* clean up xlog directory name, check it's absolute */        canonicalize_path(xlog_dir);        if (!is_absolute_path(xlog_dir))        {            fprintf(stderr, _("%s: WAL directory location must be "                              "an absolute path\n"), progname);            fprintf(stderr, _("Try \"%s --help\" for more information.\n"),                    progname);            exit(1);        }    }#ifndef HAVE_LIBZ    if (compresslevel != 0)    {        fprintf(stderr,                _("%s: this build does not support compression\n"),                progname);        exit(1);    }#endif    /* connection in replication mode to server */    //以复制模式连接到数据库    conn = GetConnection();    if (!conn)    {        /* Error message already written in GetConnection() */        //连接不成功        exit(1);    }    atexit(disconnect_atexit);    /*     * Set umask so that directories/files are created with the same     * permissions as directories/files in the source data directory.     * 设置umask以便目录/文件可以源数据目录的相同的权限创建     *     * pg_mode_mask is set to owner-only by default and then updated in     * GetConnection() where we get the mode from the server-side with     * RetrieveDataDirCreatePerm() and then call SetDataDirectoryCreatePerm().     * pg_mode_mask默认设置为owner-only,在GetConnection()更新,     *   在该函数中通过RetrieveDataDirCreatePerm()获得服务器端的模式,     *   然后调用SetDataDirectoryCreatePerm()函数.     */    umask(pg_mode_mask);    /*     * Verify that the target directory exists, or create it. For plaintext     * backups, always require the directory. For tar backups, require it     * unless we are writing to stdout.     * 验证目标目录是否存在,如不存在则创建该目录.     * 对于普通的备份,通常需要目录.     * 对于tar备份,除非写入到stdout,也需要目录.     */    if (format == 'p' || strcmp(basedir, "-") != 0)        verify_dir_is_empty_or_create(basedir, &made_new_pgdata, &found_existing_pgdata);    /* determine remote server's xlog segment size */    //确定远程服务器的xlog segment大小    if (!RetrieveWalSegSize(conn))        exit(1);    /* Create pg_wal symlink, if required */    //如需要,创建pg_wal目录链接    if (xlog_dir)    {        char       *linkloc;        //        verify_dir_is_empty_or_create(xlog_dir, &made_new_xlogdir, &found_existing_xlogdir);        /*         * Form name of the place where the symlink must go. pg_xlog has been         * renamed to pg_wal in post-10 clusters.         * pg_xlog在PG 10+后已重命名为pg_wal         */        linkloc = psprintf("%s/%s", basedir,                           PQserverVersion(conn) < MINIMUM_VERSION_FOR_PG_WAL ?                           "pg_xlog" : "pg_wal");#ifdef HAVE_SYMLINK        if (symlink(xlog_dir, linkloc) != 0)        {            fprintf(stderr, _("%s: could not create symbolic link \"%s\": %s\n"),                    progname, linkloc, strerror(errno));            exit(1);        }#else        fprintf(stderr, _("%s: symlinks are not supported on this platform\n"), progname);        exit(1);#endif        free(linkloc);    }    //执行备份    BaseBackup();    //成功标记    success = true;    return 0;}/* *  set_pglocale_pgservice * *  Set application-specific locale and service directory *  设置应用相关的locale和service目录 * *  This function takes the value of argv[0] rather than a full path. *  该函数只提取argv[0]的值而不是全路径 * * (You may be wondering why this is in exec.c.  It requires this module's * services and doesn't introduce any new dependencies, so this seems as * good as anyplace.) * (你可能会感觉疑惑:该函数在exec.c文件中) * 这需要该模块中的服务,而不需要介绍其他新的依赖,因此看起来与其他地方没有什么区别. */voidset_pglocale_pgservice(const char *argv0, const char *app){    char        path[MAXPGPATH];    char        my_exec_path[MAXPGPATH];    char        env_path[MAXPGPATH + sizeof("PGSYSCONFDIR=")];  /* longer than                                                                 * PGLOCALEDIR */    char       *dup_path;    /* don't set LC_ALL in the backend */    //不要在后台进程中设置LC_ALL    if (strcmp(app, PG_TEXTDOMAIN("postgres")) != 0)    {        setlocale(LC_ALL, "");        /*         * One could make a case for reproducing here PostmasterMain()'s test         * for whether the process is multithreaded.  Unlike the postmaster,         * no frontend program calls sigprocmask() or otherwise provides for         * mutual exclusion between signal handlers.  While frontends using         * fork(), if multithreaded, are formally exposed to undefined         * behavior, we have not witnessed a concrete bug.  Therefore,         * complaining about multithreading here may be mere pedantry.         * 在这里可以重新执行PostmasterMain()的测试,检查进程是否多线程.         * 与postmaster不同,没有前台程序调用sigprocmask(),         *   否则的话需要为多个信号控制器提供mutual exclusion.         * 如为多线程方式,则前台程序使用fork(),会导致不可预测的行为,我们不会有这种严重的bug.         * 因此,在这里对于多线程方式报错是多余的.         */    }    if (find_my_exec(argv0, my_exec_path) < 0)        return;#ifdef ENABLE_NLS    get_locale_path(my_exec_path, path);    bindtextdomain(app, path);    textdomain(app);    if (getenv("PGLOCALEDIR") == NULL)    {        //------ PGLOCALEDIR        /* set for libpq to use */        //设置libpq        snprintf(env_path, sizeof(env_path), "PGLOCALEDIR=%s", path);        canonicalize_path(env_path + 12);        dup_path = strdup(env_path);        if (dup_path)            putenv(dup_path);    }#endif    if (getenv("PGSYSCONFDIR") == NULL)    {        //---- PGSYSCONFDIR        get_etc_path(my_exec_path, path);        /* set for libpq to use */        snprintf(env_path, sizeof(env_path), "PGSYSCONFDIR=%s", path);        canonicalize_path(env_path + 13);        dup_path = strdup(env_path);        if (dup_path)            putenv(dup_path);    }}

到此,相信大家对"PostgreSQL的备份工具pg_basebackup源码中的主函数分析"有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

0