PostgreSQL启动main函数都干了什么(一)

DB Version:9.5.3

环境:CentOS7.x

调试工具:GDB

source:src/backend/main/main.c

 

 56 /*
 57  * Any Postgres server process begins execution here.
 58  */
 59 int
 60 main(int argc, char *argv[])
 61 {
 62         bool            do_check_root = true;
 63 sleep(30);
 64         progname = get_progname(argv[0]);

 修改一下代码,睡它30s。或者执行postgres可执行文件,set args 也OK。

启动数据库:

/usr/local/psql-9.5.3/bin/pg_ctl -D db2/ -l logfile  start -m fast

 

 查看后台进程PID:

[postgres@localhost ~]$ ps -ef |grep postgres
root      57843  57805  0 10:58 pts/1    00:00:00 su - postgres
postgres  57844  57843  0 10:58 pts/1    00:00:00 -bash
postgres  57977      1  0 11:01 pts/1    00:00:00 /usr/local/psql-9.5.3/bin/postgres -D db2
postgres  57981  57844  0 11:02 pts/1    00:00:00 ps -ef
postgres  57982  57844  0 11:02 pts/1    00:00:00 grep --color=auto postgres 

 

进入调试模式,需要等30s:

cgdb -p 57977

(gdb) b main.c:64
Breakpoint 1 at 0x676229: file main.c, line 64.
(gdb) c
Continuing.

Breakpoint 1, main (argc=3, argv=0x7ffceddcbe88) at main.c:64
(gdb) 

 

首先pg进入main函数,最先是获取到progname:

(gdb) p	argv[0]
$1 = 0x7ffceddcd6d6 "/usr/local/psql-9.5.3/bin/postgres"

其实这个里面还是根据main函数的第一个参数进行字符串拆分计算出progname

具体可以跟一下函数"get_progname":

(gdb) p	progname
$15 = 0x14f9010 "postgres"

 

初始化内存(MemoryContextInit):

这个是数据库启动的时候初始化的第一块内存,我们一起来看看里面的内容。

在看这个之前,我们先来了解一下PG几个关于内存的结构体

typedef struct MemoryContextData *MemoryContext;

typedef struct MemoryContextData
{
	NodeTag		type;			/* identifies exact kind of context */
	/* these two fields are placed here to minimize alignment wastage: */
	bool		isReset;		/* T = no space alloced since last reset */
	bool		allowInCritSection;		/* allow palloc in critical section */
	MemoryContextMethods *methods;		/* virtual function table */
	MemoryContext parent;		/* NULL if no parent (toplevel context) */
	MemoryContext firstchild;	/* head of linked list of children */
	MemoryContext nextchild;	/* next child of same parent */
	char	   *name;			/* context name (just for debugging) */
	MemoryContextCallback *reset_cbs;	/* list of reset/delete callbacks */
} MemoryContextData;

我们先看看MemoryContextData是如何被初始化的:

函数 MemoryContextCreate

        MemSet(node, 0, size);
	node->type = tag;
	node->methods = methods;
	node->parent = NULL;		/* for the moment */
	node->firstchild = NULL;
	node->nextchild = NULL;
	node->isReset = true;
	node->name = ((char *) node) + size;
	strcpy(node->name, name);    

(gdb) p *node
$31 = {type = T_AllocSetContext, isReset = 1 '\001', allowInCritSection = 0 '\000', methods = 0xd1bbe0 <AllocSetMethods>, parent = 0x0, firstchild = 0x0, nextchild = 0x0, name = 0x
14f9c70 "TopMemoryContext", reset_cbs = 0x0}

 其实这里最重要的是methods这个参数,这是个函数指针,还有里面其实最主要的就是内存上下文的父子关系

 我们回头再细研究这个东东。

 

函数返回的是MemoryContext转成AllocSet.就是下面的结构体,其实MemoryContextData成了它的header。

这就是后面的NODE那个大enum,直接小转大。

typedef struct AllocSetContext
{
	MemoryContextData header;	/* Standard memory-context fields */
	/* Info about storage allocated in this context: */
	AllocBlock	blocks;			/* head of list of blocks in this set */
	AllocChunk	freelist[ALLOCSET_NUM_FREELISTS];		/* free chunk lists */
	/* Allocation parameters for this context: */
	Size		initBlockSize;	/* initial block size */
	Size		maxBlockSize;	/* maximum block size */
	Size		nextBlockSize;	/* next block size to allocate */
	Size		allocChunkLimit;	/* effective chunk size limit */
	AllocBlock	keeper;			/* if not NULL, keep this block over resets */
} AllocSetContext;

typedef AllocSetContext *AllocSet;

我们来看看这个set内容,里面包含了数据库初始化的数据块大小,最大块,下一个块以及chunk的limit.

TopMemoryContext = AllocSetContextCreate((MemoryContext) NULL,
                                             "TopMemoryContext",
                                             0,
                                             8 * 1024,
                                             8 * 1024);

在初始化"TopMemoryContext"的时候,默认以及设定了最小块为0,初始化为8*1024,最大为8*1024

 

(gdb) p    *set
$45 = {header = {type = T_AllocSetContext, isReset = 1 '\001', allowInCritSection = 0 '\000', methods = 0xd1bbe0 <AllocSetMethods>, parent = 0x0, firstchild = 0x0, nextchild = 0x0,
 name = 0x14f9c70 "TopMemoryContext", reset_cbs = 0x0}, blocks = 0x0, freelist = {0x0 <repeats 11 times>}, initBlockSize = 8192, maxBlockSize = 8192, nextBlockSize = 8192, allocChunkLimit = 1024, keeper = 0x0}

可以到初始化的数据块为8K,最大8K,allocChunkLimit为1024.

这样就把这个"TopMemoryContext"初始化完成了,然后把该内存上下文赋值给CurrentMemoryContext。

(gdb) p CurrentMemoryContext
$58 = (MemoryContext) 0x14f9bb0
(gdb) p    TopMemoryContext 
$59 = (MemoryContext) 0x14f9bb0
(gdb) 

 

现在初始化"TopMemoryContext"的第一个孩子,"ErrorContext"。

处理方式跟上面的区别,就是parent是"TopMemoryContext",并且内存上下文是通过MemoryContext->methods->AllocSetAlloc这个函数指针来分配内存的

这个后面要单独分析。

(gdb) p    *ErrorContext
$65 = {type = T_AllocSetContext, isReset = 1 '\001', allowInCritSection = 0 '\000', methods = 0xd1bbe0 <AllocSetMethods>, parent = 0x14f9bb0, firstchild = 0x0, nextchild = 0x0, nam
e = 0x14f9d80 "ErrorContext", reset_cbs = 0x0}
(gdb) p    *ErrorContext->parent 
$66 = {type = T_AllocSetContext, isReset = 0 '\000', allowInCritSection = 0 '\000', methods = 0xd1bbe0 <AllocSetMethods>, parent = 0x0, firstchild = 0x14f9cc0, nextchild = 0x0, nam
e = 0x14f9c70 "TopMemoryContext", reset_cbs = 0x0}
(gdb) p *ErrorContext->parent->firstchild
$67 = {type = T_AllocSetContext, isReset = 1 '\001', allowInCritSection = 0 '\000', methods = 0xd1bbe0 <AllocSetMethods>, parent = 0x14f9bb0, firstchild = 0x0, nextchild = 0x0, nam
e = 0x14f9d80 "ErrorContext", reset_cbs = 0x0}
(gdb) 

这样就把ErrorContext初始化完成了。PG中所有的内存上下文都挂载"TopMemoryContext"下面。

 

 

 

 

后面就是大量的环境变量设置了,以及root校验。

最后调用函数"PostmasterMain(argc,argv)"。这就是我们的大管家,后面再写这部分。

 

posted @ 2016-09-02 16:21  Li.Sang  阅读(562)  评论(0编辑  收藏  举报