• 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 '01', allowInCritSection = 0 '00', 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 '01', allowInCritSection = 0 '00', 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 '01', allowInCritSection = 0 '00', 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 '00', allowInCritSection = 0 '00', 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 '01', allowInCritSection = 0 '00', methods = 0xd1bbe0 <AllocSetMethods>, parent = 0x14f9bb0, firstchild = 0x0, nextchild = 0x0, nam
    e = 0x14f9d80 "ErrorContext", reset_cbs = 0x0}
    (gdb) 

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

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

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

  • 相关阅读:
    ADSL PPPoE出错详解及宽带连接中的一些错误代码含义
    2007年世界顶级防火墙排名(附下载地址)
    Asp.net Mvc问题索引
    .NET 操作GPRS Model的类库 ATSMS
    .NET 3.5多个工程编译的DOS命令
    Google Chrome浏览器JS执行效率惊人 实测比IE快十几倍
    FTP文件同步工具(FTP_File_Synchronizer) 源代码
    [转载] ORACLE中SQL查询优化研究
    ext的grid导出为excel 方法
    数据库分页SQL语句
  • 原文地址:https://www.cnblogs.com/sangli/p/5833141.html
Copyright © 2020-2023  润新知