postgresql从入门到菜鸟(十一)initdb流程分析-初期化集簇(上)

接下来分析数据集簇初期化的过程,初期化的过程很长,并且过程中有很多相似的操作,所以在初期化中就不再一一分析函数实现了,有兴趣的朋友可以去阅读以下源码。

初期化的入口为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");

因为这个流程比较长,剩下的内容等到下篇文章再分析

你可能感兴趣的:(postgresql)