- 浏览: 230915 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
chenxliang:
2016年10月26、27日,上海浦东,Postgres中国用 ...
PostgreSQL的用户、角色和权限管理 -
gejiod:
最近出了SQL:2011標準,希望樓主也更新下
SQL2008标准 -
lincon77:
太长了,又是E文,要是有些例子可能好理解些
SQL2003标准 -
少主丶无翼:
很谢,找了很久
SQL2003标准 -
zeeeitch:
...
PostgreSQL启动过程中的那些事七:初始化共享内存和信号二十:shmem中初始化堆同步扫描、pg子进程相关结构
pg启动过程中的那些事一:初始化TopMemoryContext和ErrorContext
- 博客分类:
- PostgreSQL内核
1先上个示意图,看一下函数调用过程梗概,中间略过部分细节
前面标1的是初始化TopMemoryContext
前面标2的是初始化ErrorContext
初始化TopMemoryContext和ErrorContext的方法调用流程图
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中有三个变量的类型分别是MemoryContextData、AllocBlock、AllocChunk,这些类型是pg管理AllocSet和MemoryContext涉及内存机制的主要元素,搭起pg管理AllocSet和MemoryContext涉及的内存的架构,后面会逐个提到。 在AllocSetContextCreate()函数中,刚声明一个AllocSet类型的变量conext,马上就调用MemoryContextCreate()函数,代码见下。MemoryContextCreate()函数从名字就能看出来是创建MemoryContext的,创建一个MemoryContext类型的值后返回,返回后做了类型强制转换为AllocSet赋给context。调用MemoryContextCreate()函数创建MemoryContext时传的第二个参数是要创建类型的大小,这里取的就是AllocSetContext的大小,而不是MemoryContext的大小。
context = (AllocSet) MemoryContextCreate(T_AllocSetContext,
sizeof(AllocSetContext),
&AllocSetMethods,
parent,
name);
MemoryContextCreate()函数主要是初始化MemoryContext,MemoryContext的类型是“MemoryContextData *”,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,接着是其它成员,这样要是把TopMemoryContext当MemoryContextData类型处理,就是上面那个图,图中黑色部分相当于未知,不用管它,剩下的就是MemoryContextData类型,当TopMemoryContext是AllocSetContext类型时,就面的图,它就是个AllocSetContext类型的变量了。这样做的好处是在一个结构里既能管理内存空间,又能管理内存空间里的内容,而且逻辑功能划分很清晰。 哦,有人说从中看出了面向对象编程中继承的影子,嗯、啊,是有点,如果MemoryContextData是父类,AllocSetContext是MemoryContextData的子类,看起来是挺像的,从内存布局上看子类里也少了指向vitual table 的member function /viturl member function pointer(vptr)。看来从面向对象编程到面向过程编程是技术的渐进,就像设备的升级改造。扯的有点远了,打住。 再看上图的右边,就是上面提到的vitual table,这个是调用MemoryContextCreate()传进去的第三个参数AllocSetMethods,这是一个MemoryContextMethods类型的静态全局变量, 根据这个变量的定义(在下面)就可以看出这是一个为AllocSet Context管理内存的虚拟函数的表(vitual function table),MemoryContextMethods结构里定义了这些函数原型。这些函数实现了pg对内存的管理,整个就是pg的AllocSet和MemoryContext的内存空间管理机制。
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()的代码,这个很简洁,就是初始化TopMemoryContext和ErrorContext,完事。为了省地,把注释删了。在AllocSetContextCreate()中对TopMemoryContext和ErrorContext的处理基本上是一样的。在MemoryContextCreate()中则有些不同,判断TopMemoryContext非空后就去调用另一个函数MemoryContextAlloc(),这个函数没干什么事,直接调用上面提到的虚拟方法表中的Alloc函数,它的具体实现是AllocSetAlloc()函数,在其中分配好空间,初始化和设置AllocBlock和AllocChunk的属性,然后返回。接着在MemorycontextCreate()处理MemoryContextData结构的属性,其中把ErrorContext的parent指向TopMemoryContext,处理完MemoryContextData的属性后返回返回到AllocSetContextCreate(),设置AllocSetContext的属性,这样就完成了ErrorContext的初始化。 AllocSetAlloc()这个函数是pg中实际处理AllocSet和MemoryContext涉及的内存空间分配的函数,这个分配机制比较复杂,还涉及到了另外两个结构AllocBlock和AllocChunk,写pg的内存空间分配机制时再写吧。 下面上初始化完以后的内存结构图
TopMemoryContext和ErrorContext的内存结构图一
上图中紫色的那一块是AllocBlock,中间浅紫色的那一小块是AllocChunk。
只有两个MemoryContext图就看着有点眼花缭乱了,看来以后得画抽象图了。 pg的内存管理主要是棵树,TopMemoryContext是树根,后面再建的MemoryContext就在根的下面。pg的内存管理实际是一个图,其主要部分是树形结构,另外子节点的partent指向了TopMemoryContext,形成了环。这样整个构成了图。这样的结构便于数据库的内存管理。 AllocSetContext中blocks记录了分配给该Context的内存空间块AllockBlock的单向链表,freelist是空闲AllocChunk的链表数组。pg中给context分配内存时先分配AllockBlock,在AllockBlock里再分配AllocChunk,就是说AllocChunk是比AllockBlock小一级的内存空间单位。 上图中的ErrorContext所占的内存空间实际上在浅紫色的AllocChunk里,不是在外边,到写内存分配的时候再深入。上图是为了看清楚AllocBlock和AllocChunk,没有把ErrorContext放到AllocChunk里,实际见下面这个图。
该图引用至http://blog.sciencenet.cn/home.php?mod=space&uid=419883&do=blog&id=308964
结束
发表评论
-
PostgreSQL服务过程中的那些事三:pg服务进程中的内存上下文
2012-12-31 15:07 2062题外话:年底了,就以这篇博文结束2012 ... -
PostgreSQL服务过程中的那些事二:Pg服务进程处理简单查询六:执行器执行
2012-11-07 20:13 1821话说 查询“ select c ... -
PostgreSQL服务过程中的那些事二:Pg服务进程处理简单查询五:规划成plantree
2012-10-31 20:37 1730话说 查询“ select cname, comp ... -
PostgreSQL服务过程中的那些事二:Pg服务进程处理简单查询四:分析重写成querytree
2012-10-24 19:27 1436话说 查询“ select cname, comp ... -
postgresql 小技巧
2012-10-16 19:36 1375Note : #PostgreSQL and ... -
PostgreSQL服务过程中的那些事二:Pg服务进程处理简单查询三:获取内存快照
2012-10-16 19:31 1762话说 查询“ select cname, comp ... -
PostgreSQL服务过程中的那些事二:Pg服务进程处理简单查询二:SQL解析为parsetree
2012-10-09 19:50 1526话说 查询“ select cname, comp fr ... -
PostgreSQL服务过程中的那些事二:Pg服务进程处理简单查询一:开启事务
2012-09-25 19:55 1885在《 PostgreSQL 服务过程中的那些事二: ... -
PostgreSQL服务过程中的那些事一:启动postgres服务进程一.八:加载DB基础设施,完成服务进程初始化
2012-09-18 21:02 1803话说调用 InitPostgres ... -
PostgreSQL服务过程中的那些事一:启动postgres服务进程一.七:初始化portal管理环境
2012-09-11 19:58 1631话说调用 In ... -
PostgreSQL服务过程中的那些事一:启动postgres服务进程一.六:初始化系统表缓存catcache
2012-09-04 20:51 1856话说调用 InitPostgres ... -
PostgreSQL服务过程中的那些事一:启动postgres服务进程一.五:初始化relcache管理环境
2012-08-28 20:47 1314话说调用 InitPostgres ... -
PostgreSQL服务过程中的那些事一:启动postgres服务进程三:初始化relcache管理环境
2012-08-28 20:46 0<!-- [if gte mso 9]><x ... -
PostgreSQL服务过程中的那些事二:Pg服务进程处理简单查询梗概
2012-08-21 21:04 1258话说客户端发起请求, pg 服务器为该请求启动一个 ... -
PostgreSQL服务过程中的那些事一:启动postgres服务进程二:建立连接完成
2012-08-13 18:50 15773 这节主要讨论 pg 服务进程 pos ... -
PostgreSQL服务过程中的那些事一:启动postgres服务进程一
2012-08-08 14:42 5686到pg 服务进程了,打算搞一个完整但简单的查询例子,从 ... -
PostgreSQL启动过程中的那些事十九:walwriter进程二
2012-08-03 16:53 13793 这节主要讨论 walwrit ... -
PostgreSQL启动过程中的那些事十九:walwriter进程一
2012-08-01 17:26 1612话说 main()->Po ... -
PostgreSQL启动过程中的那些事十八:bgwriter进程二
2012-07-27 07:25 14303 这节主要讨论 bgwr ... -
PostgreSQL启动过程中的那些事十八:bgwriter进程一
2012-07-23 20:18 1641话说 main()->Postm ...
相关推荐
MyBatis 环境的初始化过程中,SqlSessionFactoryBuilder 会调用 XMLConfigBuilder 读取所有的 MybatisMapConfig.xml 和所有的 *Mapper.xml 文件,构建 MyBatis 运行的核心对象 Configuration 对象,然后将该 ...
在这个框架中,`doSomething`函数在遇到错误时抛出一个`ErrorContext`对象,包含错误代码和附加信息。捕获异常时,可以使用`getErrorMessage`函数获取易于理解的错误消息。 博客中的"ErrorCodeFramework.zip"可能...
Mybatis的设计非常注重模块化,它将SQL映射与Java代码分离,通过XML或注解来配置映射语句,使得SQL的编写和Java代码的分离,提高了代码的可维护性和可读性。在源码中,可以看到SqlSessionFactory、Executor、...
MyBatis使用简单的XML或注解用于配置和原始映射,将接口和Java的POJOs(Plan Old Java Objects,普通的Java对象)映射成数据库中的记录. orm工具的基本思想 无论是用过的hibernate,mybatis,你都可以法相他们有一个...
6. 序列化匿名类型在开发过程中,有时我们需要快速创建一个临时对象进行JSON序列化,这时匿名类型就派上用场了。例如: ```csharp var anonymousData = new { Name = "John Doe", Age = 30 }; var json = ...
4. **遵循最佳实践**:在开发过程中,遵循Mybatis的最佳实践和官方文档,可以避免许多常见问题。 通过以上步骤和策略,你应该能够有效地解决`org.apache.ibatis.exceptions.PersistenceException`这类问题,确保...
自定义按钮外观、链接目标、文本和工具提示本地化。 请注意, DNN 反馈模块将包含所有查询字符串参数的整个 URL 存储在 UserAgent 字段中,该字段可通过将 [Feedback:UserAgent] 标签添加到项目模板来从反馈评论...
在MyBatis中,封装数据是一种将数据封装成对象的过程。这种封装可以帮助我们更方便地访问和操作数据。MyBatis提供了多种方式来封装数据,例如使用ResultMap、 resultMap、association、collection等标签。 ...
卷 文档 的文件夹 PATH 列表 卷序列号为 000C-BB91 E:. │ config.properties │ Dao.java │ GeneratorDemo.java │ hibernate.cfg.xml │ HibernateDaoImpl.java │ HibernateSessionFactory.java ...