接下来分析数据集簇初期化的过程,初期化的过程很长,并且过程中有很多相似的操作,所以在初期化中就不再一一分析函数实现了,有兴趣的朋友可以去阅读以下源码。
初期化的入口为initialize_data_directory()函数,下面开始分析
一进initialize_data_directory()函数,首先进行对信号量的处理,对于信息量的类型这里就不阐述了,可以通过百度查询
void
setup_signals(void)
{
/* some of these are not valid on Windows */
#ifdef SIGHUP//正常终了情况可能会发送该信号
pqsignal(SIGHUP, trapsig);
#endif
#ifdef SIGINT//ctrl+c信号可能会发送该信号
pqsignal(SIGINT, trapsig);
#endif
……
}
接下来创建pgdata目录
void
create_data_directory(void)
{
int ret;
switch ((ret = pg_check_dir(pg_data)))
{
case 0:
/* PGDATA not there, must create it */
printf(_("creating directory %s ... "),
……
case 1:
……
if (chmod(pg_data, S_IRWXU) != 0)
{
fprintf(stderr, _("%s: could not change permissions of directory \"%s\": %s\n"),
progname, pg_data, strerror(errno));
exit_nicely();
}
……
//这里会判断目录是否有权限,或者目录是否为空等情况
}
}
接下来创建pgdata目录下xlog目录,其中主要也是判断权限,路径等问题,就不展开函数了
create_xlog_or_symlink();
接下来创建pgdata下的其他子目录
for (i = 0; i < lengthof(subdirs); i++)
{
//subdir这个列表在前期准备篇中提到过,里面存放需要创建的子目录的名称
char *path;
path = psprintf("%s/%s", pg_data, subdirs[i]);
if (mkdir(path, S_IRWXU) < 0)
{
fprintf(stderr, _("%s: could not create directory \"%s\": %s\n"),
progname, path, strerror(errno));
exit_nicely();
}
free(path);
}
创建PG_VERSION文件
write_version_file(NULL);//在pgdata下创建PG_VERSION,内容为9.6
创建一个空的postgresql.conf文件
set_null_conf();
之前的文章中提到过,initdb需要调用postgres,下面开始调用postgres确定shared_buffers和max_connections的值
//test_config_settings(void)函数用来确定max_connections 和 shared_buffers的值
static void
test_config_settings(void)
{
……
#define MIN_BUFS_FOR_CONNS(nconns) ((nconns) * 10)
//定义max_connections 和 shared_buffers的尝试区间
static const int trial_conns[] = {
100, 50, 40, 30, 20, 10
};
static const int trial_bufs[] = {
16384, 8192, 4096, 3584, 3072, 2560, 2048, 1536,
1000, 900, 800, 700, 600, 500,
400, 300, 200, 100, 50
};
……
for (i = 0; i < connslen; i++)
{
test_conns = trial_conns[i];
test_buffs = MIN_BUFS_FOR_CONNS(test_conns);
//下面拼装了一个cmd命令
//根据调试结果,几个参数的值如下
//backend_exec:F:\postgresql-9.6.10\bin\bin\postgres
//boot_options:-F
//test_buffs:1000 之前有过宏定义,值为test_conns*10
//test_conns:100 即trial_conns[0]
//最终命令: "F:\postgresql-9.6.10\bin\bin\postgres" --boot -x0 -F -c max_connections=100
// -c shared_buffers=1000 < "/dev/null" > "/dev/null" 2>&1"
snprintf(cmd, sizeof(cmd),
"\"%s\" --boot -x0 %s "
"-c max_connections=%d "
"-c shared_buffers=%d "
"-c dynamic_shared_memory_type=none "
"< \"%s\" > \"%s\" 2>&1",
backend_exec, boot_options,
test_conns, test_buffs,
DEVNULL, DEVNULL);
//这里的system函数,会根据shared_buffers和max_connections值去fork一个postgres的子进程
//然后,然后返回fork的结果,判断shared_buffers和max_connections是否可行
status = system(cmd);
if (status == 0)
{
ok_buffers = test_buffs;
break;
}
}
……
//后面shared_buffers设置和max_connections 同理,就不再贴代码了
}
接下来将相应的参数写到 postgresql.conf 、pg_hba.conf等文件
setup_config()//这里就没有调用postgres了,只是单纯的写入,就不展开分析了
接下来的步骤就很重要了,初期化bootstrap_template1数据库
这里大概提一下后面的流程,和template
流程:初期化template1数据库-》拷贝生成template0数据库-》拷贝生成postgres数据库
template1:默认的数据库模板,可以连接和创建对象
template0:相当于纯净版本的备份,不可以连接和创建对象
然后代码分析之前还要说明一个文件postgres.bki
BKI是一种特殊脚本,在引导模式的时候会被后台解析,文件位于share目录下
然后看一下文件内容:
create pg_proc 1255 bootstrap rowtype_oid 81
(
proname = name ,
pronamespace = oid ,
proowner = oid ,
……
)
insert OID = 1242 ( boolin 11 10 12 1 0 0 0 f f f f t f i s 1 0 16 "2275" _null_ _null_ _null_ _null_ _null_ boolin _null_ _null_ _null_ )
insert OID = 1243 ( boolout 11 10 12 1 0 0 0 f f f f t f i s 1 0 2275 "16" _null_ _null_ _null_ _null_ _null_ boolout _null_ _null_ _null_ )
insert OID = 1244 ( byteain 11 10 12 1 0 0 0 f f f f t f i s 1 0 17 "2275" _null_ _null_ _null_ _null_ _null_ byteain _null_ _null_ _null_ )
……
这段可以理解为创建了pg_proc表,并向pg_proc中插入了数据。后面还会创建pg_class等系统表,并向其中插入数据。
还有一些比较特殊的
create pg_attribute 1249 bootstrap without_oids rowtype_oid 75
(
attrelid = oid ,
attname = name ,
atttypid = oid ,
……
)
insert ( 1255 proname 19 -1 NAMEDATALEN 1 0 -1 -1 f p c t f "" f t 0 0 _null_ _null_ _null_ )//这里NAMEDATALEN其实是宏的名称
insert ( 1255 pronamespace 26 -1 4 2 0 -1 -1 t p i t f "" f t 0 0 _null_ _null_ _null_ )
……
下面开始分析代码
static void
bootstrap_template1(void)
{
……
//读取postgres.bki文件
//这边的BKI是一种特殊脚本,在引导模式的时候会被后台解析
//postgres.bki文件在\src\backend\catalog目录下
bki_lines = readfile(bki_file);
/* Check that bki file appears to be of the right version */
……
//这里还有一些权限和版本的判断,代码就不分析了
//下面这段比较有意思,上面分析过在向pg_attribute插入数据时,第五个参数是宏的名称
//这里会用宏的值去替换宏名称,比如上面的NAMEDATALEN就被替换成了64
sprintf(buf, "%d", NAMEDATALEN);
bki_lines = replace_token(bki_lines, "NAMEDATALEN", buf);
……
//到这里,文件的分析和处理就完了了,下面还要拼接一下postgres命令的字符串
//这里第一个参数依旧是postgres的可执行程序,不过加了--boot参数,意思进入引导模式(bootstrap模式)
//这个模式官方手册中并没有过多介绍,似乎也只会在initdb阶段使用
//引导模式的入口是AuxiliaryProcessMain()函数,有兴趣的朋友可以去看一看
snprintf(cmd, sizeof(cmd),
"\"%s\" --boot -x1 %s %s",
backend_exec, boot_options, talkargs);
//下面就开始执行上面拼接好的字符串了
PG_CMD_OPEN;
for (line = bki_lines; *line != NULL; line++)
{
PG_CMD_PUTS(*line);
free(*line);
}
PG_CMD_CLOSE;
free(bki_lines);
// call exit_nicely() if we got a signal, or else output "ok".
//这里的check_ok()其实就是对 上面postgres命令执行过程中信号量的处理
check_ok();
}
在template1模板创建好之后在其目录下创建一个版本信息文件
write_version_file("base/1");
因为这个流程比较长,剩下的内容等到下篇文章再分析