PostgreSQL的备份工具pg_basebackup源码中的主函数分析
本篇内容主要讲解“PostgreSQL的备份工具pg_basebackup源码中的主函数分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“PostgreSQL的备份工具pg_basebackup源码中的主函数分析”吧!
10年积累的网站建设、成都网站设计经验,可以快速应对客户对网站的新想法和需求。提供各种问题对应的解决方案。让选择我们的客户得到更好、更有力的网络服务。我虽然不认识你,你也不认识我。但先网站设计制作后付款的网站建设流程,更有南召免费网站建设让你可以放心的选择与我们合作。
一、数据结构
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_OPTERR int 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()函数进行备份.
int main(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文件中) * 这需要该模块中的服务,而不需要介绍其他新的依赖,因此看起来与其他地方没有什么区别. */ void set_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源码中的主函数分析”有了更深的了解,不妨来实际操作一番吧!这里是创新互联网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
当前文章:PostgreSQL的备份工具pg_basebackup源码中的主函数分析
分享网址:http://ybzwz.com/article/jjiojd.html