一、 GUC参数简介
1. 参数分类
按生效时分类,主要有以下6类(guc.h文件):
typedef enum
{
PGC_INTERNAL, // 只能通过内部进程设置的参数,用户不能设置
PGC_POSTMASTER, // 只能在postmaster启动时,通过读取配置文件或解析命令行参数配置(重启生效)
PGC_SIGHUP, // 只能在postmaster启动时,或者改变配置文件后发送SIGHUP信号设置(重启或执行pg_reload_conf();生效)
PGC_SU_BACKEND, //只能在postmaster启动时,或者客户端新建连接时生效,已建立的连接会忽略(这类参数只能由超级用户设置)
PGC_BACKEND, // 只能在postmaster启动时,或者客户端新建连接时生效,已建立的连接会忽略
PGC_SUSET, // 只能在postmaster启动时,或者由超级用户通过SQL(set命令)设置
PGC_USERSET // 任何用户任何时候均可配置
} GucContext;
2. 参数来源
按照优先级从低到高的顺序排列,相同参数优先级更高的来源值会生效。
typedef enum
{
PGC_S_DEFAULT, /* 默认值 */
PGC_S_DYNAMIC_DEFAULT, /* 通过初始化计算得到 */
PGC_S_ENV_VAR, /* 来自环境变量 */
PGC_S_FILE, /* 来自postgresql.conf */
PGC_S_ARGV, /* 来自postmaster命令行 */
PGC_S_GLOBAL, /* 数据库全局设置 */
PGC_S_DATABASE, /* 数据库安装时设置 */
PGC_S_USER, /* 用户设置 */
PGC_S_DATABASE_USER, /* 用户在单个数据库中的设置*/
PGC_S_CLIENT, /* 通过客户端连接请求传送过来的数据包设置 */
PGC_S_OVERRIDE, /* 特殊场景强制覆盖默认值 */
PGC_S_INTERACTIVE, /* 仅作为错误报告的分界线 */
PGC_S_TEST, /* 仅用作测试 */
PGC_S_SESSION /* SET命令设置 */
} GucSource; // 以上来源优先级从低到高
3. 参数组成
每种类型的GUC参数都有两部分组成:共性部分+特性部分。
- 共性部分:由config_generic结构体描述
- 特性部分:每种具体数据类型(boolean,int,float,string)的参数都有对应结构体(例如config_int),且结构体的第一项都是指向共性部分的指针。
共性部分代码(以下均在guc_tables.h)
struct config_generic
{
/* constant fields, must be set correctly in initial value
常量区域,必须正确设置为初始化值 */
const char *name; /* 参数名 - MUST BE FIRST */
GucContext context; /* 参数类型(按生效时间,即前面提到的GucContext) */
enum config_group group; /* 参数分组 */
const char *short_desc; /* 简单描述 */
const char *long_desc; /* 详细描述 */
int flags; /* 标志位, see guc.h */
/* variable fields, initialized at runtime
变量区域,在运行时初始化 */
enum config_type vartype; /* 参数值数据类型 */
int status; /* 参数状态 */
GucSource source; /* 参数来源 */
GucSource reset_source; /* 参数值为reset_value时的参数来源*/
GucContext scontext; /* 参数设置上下文 */
GucContext reset_scontext; /* 参数值为reset_value时的上下文 */
GucStack *stack; /* 堆栈,用于保存前一个值 */
void *extra; /* "extra" pointer 指向当前真实值 */
char *last_reported; /* if variable is GUC_REPORT, value last sent to client (NULL if not yet sent) */
char *sourcefile; /* 配置所在源文件 */
int sourceline; /* 在源文件中的行号 */
};
按数据类型分类如下:
enum config_type
{
PGC_BOOL,
PGC_INT,
PGC_REAL, // 实数
PGC_STRING,
PGC_ENUM
};
特性部分,以config_int为例
struct config_int
{
/* 第一部分一定指向共性结构 */
struct config_generic gen;
/* constant fields, must be set correctly in initial value: */
int *variable; /* 参数当前被设置值 */
int boot_val; /* 参数初始值 */
int min; /* 参数最小值 */
int max; /* 参数最大值 */
GucIntCheckHook check_hook;
GucIntAssignHook assign_hook; /* 用于设置reset_val */
GucShowHook show_hook;
/* variable fields, initialized at runtime: */
int reset_val;
void *reset_extra;
};
4. 参数配置基本过程
- 初始化GUC参数:将参数设置为默认值
- 解析postmaster命令行参数:根据postmaster命令行参数进行配置
- 读取参数文件:根据postgresql.conf文件中的设置值再次配置
铺垫了这么多,终于又回到PostmasterMain函数上,我们接着上次的代码继续往下看。
二、 初始化GUC参数
这个对应的只有一句
/*
* Options setup
*/
InitializeGUCOptions();
opterr = 1;
InitializeGUCOptions函数主要干两件事:
- 循环调用InitializeOneGUCOption函数,为每个变量设置前面提到的共性和特性部分
- 调用InitializeGUCOptionsFromEnvironment函数:根据环境变量的值进行初始化
/*
* Initialize GUC options during program startup.
*
* Note that we cannot read the config file yet, since we have not yet
* processed command-line switches.
*/
void
InitializeGUCOptions(void)
{
int i;
/* 时区初始化 */
pg_timezone_initialize();
/*
* 该函数统计参数个数并分配相应的config_generic类型的全局指针数组guc_variables以保存每个参数结构体的地址,并且对该数据进行排序。
*/
build_guc_variables();
/*
* 遍历参数数组,并为每个参数的共性及特性结构体部分填充值
*/
for (i = 0; i < num_guc_variables; i++)
{
InitializeOneGUCOption(guc_variables[i]);
}
guc_dirty = false;
reporting_enabled = false;
/* 避免事务相关设置被覆盖 */
SetConfigOption("transaction_isolation", "read committed",
PGC_POSTMASTER, PGC_S_OVERRIDE);
SetConfigOption("transaction_read_only", "no",
PGC_POSTMASTER, PGC_S_OVERRIDE);
SetConfigOption("transaction_deferrable", "no",
PGC_POSTMASTER, PGC_S_OVERRIDE);
/* 由于历史原因,部分参数需要从环境变量中获取值 */
InitializeGUCOptionsFromEnvironment();
}
三、 解析postmaster命令行参数
- 解析postmaster命令行参数,并用于设置GUC参数值。代码很长,这里省略一部分,有点类似shell的写法。
while ((opt = getopt(argc, argv, "B:bc:C:D:d:EeFf:h:ijk:lN:nOPp:r:S:sTt:W:-:")) != -1)
{
switch (opt)
{
case 'B':
SetConfigOption("shared_buffers", optarg, PGC_POSTMASTER, PGC_S_ARGV);
break;
case 'b':
/* Undocumented flag used for binary upgrades */
IsBinaryUpgrade = true;
break;
…
case 'N':
SetConfigOption("max_connections", optarg, PGC_POSTMASTER, PGC_S_ARGV);
break;
case 'n':
/* Don't reinit shared mem after abnormal exit */
Reinit = false;
break;
…
default:
write_stderr("Try \"%s --help\" for more information.\n",
progname);
ExitPostmaster(1);
}
}
四、 读取参数文件
SelectConfigFiles函数首先在指定目录下找到配置文件,然后调用词法分析程序解析文件。对于解析到的每个参数及其参数值,调用SetConfigOption函数完成参数修改。
参数合法性检验,如果一切合法,则将当前目录转入数据目录。进行后续操作。
/*
* Locate the proper configuration files and data directory, and read postgresql.conf for the first time.
*/
if (!SelectConfigFiles(userDoption, progname))
ExitPostmaster(2);
五、 杂七杂八
下面内容比较散也比较少,合在一部分。
1. 创建数据库集群锁定文件
调用CreateDataDirLockFile()函数在数据目录建立数据库集群的lock文件postmaster.pid,这样就能保证不会对同一个数据库集群"启动两次"。虽然也会建立socket lock文件,但仍是认为数据目录的锁文件更可信。
/*
* 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.
*/
CreateDataDirLockFile(true);
/*
* Read the control file (for error checking and config info).
*/
LocalProcessControlFile(false);
/*
* Register the apply launcher.
*/
ApplyLauncherRegister();
CreateDataDirLockFile函数定义
由于调用此函数前已经将目录切换至pg数据目录,因此函数中只需使用相对目录
void
CreateDataDirLockFile(bool amPostmaster)
{
CreateLockFile(DIRECTORY_LOCK_FILE, amPostmaster, "", true, DataDir);
}
CreateLockFile这个函数很长,但它做的就是在数据目录中创建一个postmaster.pid文件
2. 共享库预加载
pg丰富的插件,其中不少就是通过共享库来实现的。这里就是调用process_shared_preload_libraries()函数来导入在shared_preload_libraries参数中指定的共享库。
/*
* process any libraries that should be preloaded at postmaster start
*/
process_shared_preload_libraries();
/*
* Initialize SSL library, if specified.
*/
#ifdef USE_SSL
if (EnableSSL)
{
(void) secure_initialize(true);
LoadedSSL = true;
}
#endif
/*
* Now that loadable modules have had their chance to register background workers, calculate MaxBackends.
*/
InitializeMaxBackends();
3. 共享内存初始化
这里调用各模块的共享内存的使用量估计函数,计算总共所需的共享内存的量并申请。详细可以看CreateSharedMemoryAndSemaphores()函数。
/*
* Set up shared memory and semaphores.
*/
reset_shared();
4. 启动syslogger子进程
/*
* If enabled, start up syslogger collection subprocess
*/
SysLoggerPID = SysLogger_Start();
参考
《PostgreSQL数据库内核分析》第二章
Postgres中postmaster代码解析(上) - JavaShuo