`

pg启动过程中的那些事一:初始化TopMemoryContext和ErrorContext

阅读更多

1先上个示意图,看一下函数调用过程梗概,中间略过部分细节

 



 

             前面标1的是初始化TopMemoryContext

             前面标2的是初始化ErrorContext

初始化TopMemoryContextErrorContext的方法调用流程图

 

2初始化TopMemoryContext的过程

 

 

话说main()->->PostmasterMain()->->MemoryContextInit()->AllocSetContextCreate()以后用“-> 表示调用AllocSetContextCreate()函数主要是初始化AllocSet类型的变量,AllocSet的类型是“AllocSetContext*”,AllocSetContext是结构,定义见下面,也就是说,AllocSetContextCreate()函数主要是初始化AllocSetContext类型的变量。

MemoryContextInit()函数中,一上来就是下面这句,调用AllocSetContextCreate初始化TopMemoryContext

 

     TopMemoryContext = AllocSetContextCreate((MemoryContext) NULL,

                                                    "TopMemoryContext",

                                                    0,

                                                    8 * 1024,

                                                    8 * 1024);

TopMemoryContext是个全局变量,定义如下

MemoryContext TopMemoryContext = NULL;

     

MemoryContextInit()函数调用过来初始化Memorycontext类型的全局变量TopMemoryContext,不是AllocSetContextCreate()函数主要是初始化AllocSetContext类型的变量吗,怎么又初始化上Memorycontext类型的全局变量TopMemoryContext了呢,先卖个关子,容后再说。这是PostgresSQL(以后简称pg)一个比较秒的地方,也涉及了后面用到的面向过程编程的一个技巧。

TopMemoryContext这个全局变量在pg有至关重要,统领全局的地位,以后自然明白。

 

AllocSetContext定义如下:

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 */

         bool           isReset;               /* T = no space alloced since last reset */

         /* Allocation parameters for this context: */

         Size            initBlockSize;      /* initial block size */

         Size            maxBlockSize;   /* maximum block size */

         Size            nextBlockSize;   /* next block size to allocate */

         AllocBlock keeper;                        /* if not NULL, keep this block over resets */

} AllocSetContext;

 

 

AllocSetContext中有三个变量的类型分别是MemoryContextDataAllocBlockAllocChunk,这些类型是pg管理AllocSetMemoryContext涉及内存机制的主要元素,搭起pg管理AllocSetMemoryContext涉及的内存的架构,后面会逐个提到。

 

AllocSetContextCreate()函数中,刚声明一个AllocSet类型的变量conext,马上就调用MemoryContextCreate()函数,代码见下。MemoryContextCreate()函数从名字就能看出来是创建MemoryContext的,创建一个MemoryContext类型的值后返回,返回后做了类型强制转换为AllocSet赋给context。调用MemoryContextCreate()函数创建MemoryContext时传的第二个参数是要创建类型的大小,这里取的就是AllocSetContext的大小,而不是MemoryContext的大小。

 

     AllocSet context;

     context = (AllocSet) MemoryContextCreate(T_AllocSetContext,

                                                    sizeof(AllocSetContext),

                                                    &AllocSetMethods,

                                                    parent,

                                                    name);

 

 

MemoryContextCreate()函数主要是初始化MemoryContextMemoryContext的类型是“MemoryContextData *”,MemoryContextData是个结构,定义见下面。

 

typedef struct MemoryContextData

{

         NodeTag           type;                            /* identifies exact kind of context */

         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) */

} MemoryContextData;

 

MemoryContextCreate()函数的简化代码如下,主要是声明一个MemoryContext类型的局部变量node,分配内存并初始化。注意,这时还没有初始化TopMemoryContext,所以node的内存空间是malloc出来的。MemoryContext类型的局部变量node初始化完后返回。

 

MemoryContext

MemoryContextCreate(NodeTag tag, Size size,

                       MemoryContextMethods *methods,

                       MemoryContext parent,

                       const char *name)

{

     MemoryContext node;

     Size     needed = size + strlen(name) + 1;

     if (TopMemoryContext == NULL)

     {

         /* Special case for startup: use good ol' malloc */

         node = (MemoryContext) malloc(needed);

         Assert(node != NULL);

     }

 

     /* Initialize the node as best we can */

     MemSet(node, 0, size);

     node->type = tag;

     node->methods = methods;

     node->parent = NULL;        /* for the moment */

     node->firstchild = NULL;

     node->nextchild = NULL;

     node->name = ((char *) node) + size;

     strcpy(node->name, name);

     /* Return to type-specific creation routine to finish up */

     return node;

}

 

 

 

 

AllocSetContextCreate()函数得到从MemoryContextCreate()函数返回的MemoryContextCreate类型的值后转换为AllocSet类型,赋给context,把context其他属性初始化。最后又把context的类型由AllocSet转换为MemoryContext后返回给调用方法,赋值给TopMemoryContext。至此TopMemoryContext初始化完成。

代码见下面。

 

 

 

MemoryContext

AllocSetContextCreate(MemoryContext parent,

         const char *name,

         Size minContextSize,

         Size initBlockSize,

         Size maxBlockSize)

{

     AllocSet context;

     /* Do the type-independent part of context creation */

     context = (AllocSet) MemoryContextCreate(T_AllocSetContext,

                                                    sizeof(AllocSetContext),

                                                    &AllocSetMethods,

                                                    parent,

                                                    name);

 

     context->initBlockSize = initBlockSize;

     context->maxBlockSize = maxBlockSize;

     context->nextBlockSize = initBlockSize;

     context->isReset = true;

     return (MemoryContext) context;

}

 

在初始化TomMemoryContext的过程中,先按AllocSet的类型初始化一个MemoryContext类型的变量,然后把这个变量转成AllocSet类型,初始化AllocSet类型的其它成员,再将其转成MemoryContext类型的变量后赋值给TopMemoryContext。为什么能做这两次类型转换呢?见下面分解。

下面上个图看看初始化出来的TopMemoryContext的结构图(以后所有内存结构图都是低地址在下面,高地址在上面,再后面有内存结构图时就不标低地址位了):

 

 



 


 

TopMemoryContext的内存结构图

 

TopMemoryContext转成AllocSet类型后的内存结构图

 

 

上图中分两块说,先看左边的大方块。其中下面深蓝色部分是MemoryContextData的内存结构,深蓝色和上面的浅颜色部分合起来是AllocSetContext的内存结构。

       初始化TopMemoryContext的时候,分配的内存大小不是按MemoryContextData结构的大小分的,是按AllocSetContext的大小分的内存,又在紧挨着上面分配了17个字节记录这个MemoryContextData /AllocSetContext 的名字“TopMemoryContext”。

       能在初始化TopMemoryContext的过程中能在类型MemoryContextData AllocSetContext之间相互转化,是利用了AllocSetContext结构布局上的特点,刚一开始就是个MemoryContextData,接着是其它成员,这样要是把TopMemoryContextMemoryContextData类型处理,就是上面那个图,图中黑色部分相当于未知,不用管它,剩下的就是MemoryContextData类型,当TopMemoryContextAllocSetContext类型时,就面的图,它就是个AllocSetContext类型的变量了。这样做的好处是在一个结构里既能管理内存空间,又能管理内存空间里的内容,而且逻辑功能划分很清晰。

哦,有人说从中看出了面向对象编程中继承的影子,嗯、啊,是有点,如果MemoryContextData是父类,AllocSetContextMemoryContextData的子类,看起来是挺像的,从内存布局上看子类里也少了指向vitual table member function /viturl member function pointervptr)。看来从面向对象编程到面向过程编程是技术的渐进,就像设备的升级改造。扯的有点远了,打住。

       再看上图的右边,就是上面提到的vitual table,这个是调用MemoryContextCreate()传进去的第三个参数AllocSetMethods,这是一个MemoryContextMethods类型的静态全局变量, 根据这个变量的定义(在下面)就可以看出这是一个为AllocSet Context管理内存的虚拟函数的表(vitual function table),MemoryContextMethods结构里定义了这些函数原型。这些函数实现了pg对内存的管理,整个就是pgAllocSetMemoryContext的内存空间管理机制。

 

 

静态全局变量AllocSetMethods的定义:

static MemoryContextMethods AllocSetMethods = {

     AllocSetAlloc,

     AllocSetFree,

     AllocSetRealloc,

     AllocSetInit,

     AllocSetReset,

     AllocSetDelete,

     AllocSetGetChunkSpace,

     AllocSetIsEmpty,

     AllocSetStats,

#ifdef MEMORY_CONTEXT_CHECKING

     ,AllocSetCheck

#endif

};

结构MemoryContextMethods的定义:

typedef struct MemoryContextMethods

{

     void    *(*alloc) (MemoryContext context, Size size);

     /* call this free_p in case someone #define's free() */

     void     (*free_p) (MemoryContext context, void *pointer);

     void    *(*realloc) (MemoryContext context, void *pointer, Size size);

     void     (*init) (MemoryContext context);

     void     (*reset) (MemoryContext context);

     void     (*delete) (MemoryContext context);

     Size     (*get_chunk_space) (MemoryContext context, void *pointer);

     bool     (*is_empty) (MemoryContext context);

     void     (*stats) (MemoryContext context);

#ifdef MEMORY_CONTEXT_CHECKING

     void     (*check) (MemoryContext context);

#endif

} MemoryContextMethods;

pg中使用这些函数的情况来看,这些方法在MemoryContext中定义,在AllocSet中实现和使用,又像子类实现父类的虚拟方法,又是面向对象编程的影子。面向过程编程中的编程技巧在面向对象编程中基本上都成了编程语言的内在机制了。台湾的侯捷先生说要写一本《c语言高级编程》的书也有十来年了吧,到现在都没出,是否有这个原因呢

至此,TopMemoryContext初始化完成了,里面的其他属性就不写了。有了前面内容垫底,初始化ErrorContext就好写多了,因为它的类型和TomMemoryContext是一样的。

 

 

 

 

3 初始化ErrorContext的过程

       先看看MemoryContextInit()的代码,这个很简洁,就是初始化TopMemoryContextErrorContext,完事。为了省地,把注释删了。在AllocSetContextCreate()中对TopMemoryContextErrorContext处理基本上是一样的。在MemoryContextCreate()中则有些不同,判断TopMemoryContext非空后就去调用另一个函数MemoryContextAlloc(),这个函数没干什么事,直接调用上面提到的虚拟方法表中的Alloc函数,它的具体实现是AllocSetAlloc()函数,在其中分配好空间,初始化和设置AllocBlockAllocChunk的属性,然后返回。接着在MemorycontextCreate()处理MemoryContextData结构的属性,其中把ErrorContextparent指向TopMemoryContext,处理完MemoryContextData的属性后返回返回到AllocSetContextCreate(),设置AllocSetContext的属性,这样就完成了ErrorContext的初始化。

       AllocSetAlloc()这个函数是pg中实际处理AllocSetMemoryContext涉及的内存空间分配的函数,这个分配机制比较复杂,还涉及到了另外两个结构AllocBlockAllocChunk,写pg的内存空间分配机制时再写吧。

       下面上初始化完以后的内存结构图

 

 




 

TopMemoryContextErrorContext的内存结构图一

 

 

上图中紫色的那一块是AllocBlock,中间浅紫色的那一小块是AllocChunk

       只有两个MemoryContext图就看着有点眼花缭乱了,看来以后得画抽象图了。

pg的内存管理主要是棵树,TopMemoryContext是树根,后面再建的MemoryContext就在根的下面。pg的内存管理实际是一个图,其主要部分是树形结构,另外子节点的partent指向了TopMemoryContext,形成了环。这样整个构成了图。这样的结构便于数据库的内存管理。

AllocSetContextblocks记录了分配给该Context的内存空间块AllockBlock的单向链表,freelist是空闲AllocChunk的链表数组。pg中给context分配内存时先分配AllockBlock,在AllockBlock里再分配AllocChunk,就是说AllocChunk是比AllockBlock小一级的内存空间单位。

上图中的ErrorContext所占的内存空间实际上在浅紫色的AllocChunk里,不是在外边,到写内存分配的时候再深入。上图是为了看清楚AllocBlockAllocChunk,没有把ErrorContext放到AllocChunk里,实际见下面这个图。

 

 



 

该图引用至http://blog.sciencenet.cn/home.php?mod=space&uid=419883&do=blog&id=308964

 

结束

 

 

 

 

  • 大小: 13.1 KB
  • 大小: 28.5 KB
  • 大小: 31.3 KB
  • 大小: 366.7 KB
  • 大小: 43.9 KB
  • 大小: 93 KB
0
0
分享到:
评论

相关推荐

    腊月一十八,聊聊 MyBatis 中的设计模式.docx

    MyBatis 环境的初始化过程中,SqlSessionFactoryBuilder 会调用 XMLConfigBuilder 读取所有的 MybatisMapConfig.xml 和所有的 *Mapper.xml 文件,构建 MyBatis 运行的核心对象 Configuration 对象,然后将该 ...

    [C++][Error Code]如何定义Error Code框架

    在这个框架中,`doSomething`函数在遇到错误时抛出一个`ErrorContext`对象,包含错误代码和附加信息。捕获异常时,可以使用`getErrorMessage`函数获取易于理解的错误消息。 博客中的"ErrorCodeFramework.zip"可能...

    1设计模式开源架构源码2021.pdf

    Mybatis的设计非常注重模块化,它将SQL映射与Java代码分离,通过XML或注解来配置映射语句,使得SQL的编写和Java代码的分离,提高了代码的可维护性和可读性。在源码中,可以看到SqlSessionFactory、Executor、...

    springmybatis

    MyBatis使用简单的XML或注解用于配置和原始映射,将接口和Java的POJOs(Plan Old Java Objects,普通的Java对象)映射成数据库中的记录. orm工具的基本思想 无论是用过的hibernate,mybatis,你都可以法相他们有一个...

    c# Newtonsoft 六个值得使用的特性(上)

    6. 序列化匿名类型在开发过程中,有时我们需要快速创建一个临时对象进行JSON序列化,这时匿名类型就派上用场了。例如: ```csharp var anonymousData = new { Name = "John Doe", Age = 30 }; var json = ...

    Mybatis报错: org.apache.ibatis.exceptions.PersistenceException解决办法

    4. **遵循最佳实践**:在开发过程中,遵循Mybatis的最佳实践和官方文档,可以避免许多常见问题。 通过以上步骤和策略,你应该能够有效地解决`org.apache.ibatis.exceptions.PersistenceException`这类问题,确保...

    R7.FeedbackButton:DNN 平台的反馈皮肤对象扩展

    自定义按钮外观、链接目标、文本和工具提示本地化。 请注意, DNN 反馈模块将包含所有查询字符串参数的整个 URL 存储在 UserAgent 字段中,该字段可通过将 [Feedback:UserAgent] 标签添加到项目模板来从反馈评论...

    mybatis查询语句揭秘之封装数据

    在MyBatis中,封装数据是一种将数据封装成对象的过程。这种封装可以帮助我们更方便地访问和操作数据。MyBatis提供了多种方式来封装数据,例如使用ResultMap、 resultMap、association、collection等标签。 ...

    前端-后端java的Util类的工具类

    卷 文档 的文件夹 PATH 列表 卷序列号为 000C-BB91 E:. │ config.properties │ Dao.java │ GeneratorDemo.java │ hibernate.cfg.xml │ HibernateDaoImpl.java │ HibernateSessionFactory.java ...

Global site tag (gtag.js) - Google Analytics