PostgreSQL共享缓冲区部分代码阅读总结(一)
2012-05-28 19:12
260 查看
http://blog.sina.com.cn/s/blog_48c95a190100grf9.html
PostgreSQL中缓冲区管理主要分为共享缓冲区和管理和本地缓冲区管理两大部分,其中共享缓冲区结构以及管理方法主要定义在文件pgsql/src/backend/storage/buffer/buf_init.c,文件pgsql/src/backend/storage/buffer/buf_table.c,文件pgsql/src/backend/storage/buffer/bufmgr.c和文件pgsql/src/backend/storage/buffer/freelist.c中。
我们首先来看文件pgsql/src/backend/storage/buffer/buf_init.c,这个文件主要就是定义了共享缓冲区管理器的初始化方法。在本地缓冲区管理器部分我们知道缓冲区的组织至少包括三个部分:缓冲区块、缓冲区描述器以及引用计数,共享缓冲区也是一样。在文件的开始就定义了这三个全局变量,唯一跟本地缓冲区不同的地方就是,这里定义的变量名前面都没有Local这个字符串,此外这里引用计数的名称前加了一个Private。
BufferDesc *BufferDescriptors;
char *BufferBlocks;
int32
*PrivateRefCount;
然后就是一些其他的用于统计信息的计数器变量:
long int
ReadBufferCount;
long int
ReadLocalBufferCount;
long int
BufferHitCount;
long int
LocalBufferHitCount;
long int
BufferFlushCount;
long int
LocalBufferFlushCount;
long int
BufFileReadCount;
long int
BufFileWriteCount;
其中包括读缓冲区的次数、缓冲区命中的次数、缓冲区文件的读写次数等等,通过这些统计数据我们就可以计算缓冲区命中率一类的更有意义的数据信息。
共享缓冲区因为会涉及到多个事务的并发访问以及日志的记录和检查点的处理,所以相较于本地缓冲区的管理更加复杂,涉及到的数据结构也更加繁琐。在这里,和本地缓冲区一样,我们首先介绍共享缓冲区的初始化。
void
InitBufferPool(void)
{
bool
foundBufs,foundDescs;
BufferDescriptors = (BufferDesc *)
ShmemInitStruct("Buffer Descriptors",
NBuffers
* sizeof(BufferDesc), &foundDescs);
BufferBlocks = (char *)
ShmemInitStruct("Buffer Blocks",
NBuffers
* (Size) BLCKSZ, &foundBufs);
调用函数ShmemInitStruct为缓冲区描述器和缓冲区块分配空间,和本地缓冲区不同的
是这里没有连同私有引用计数一起分配,函数ShmemInitStruct定义在文件pgsql/src/backend/storage/ipc/shmem.c中,它会根据第一个参数,也就是共享内存数据结构的名称来判断这个结构在共享内存中是否已经存在,如果不存在的话就在共享内存段中分配一个这样的结构,我们稍后会对这个重要的函数进行分析。
if (foundDescs || foundBufs)
{
Assert(foundDescs && foundBufs);
}
如果通过调用函数ShmemInitStruct我们发现缓冲区描述器或者缓冲区块已经分配了的
话我们就要验证这两个结构都是已经分配的,因为这两个结构肯定是一起分配的,如果一个还存在另一个已经不存在的话就说明共享内存段出了问题。
else
{
BufferDesc *buf;
int
i;
buf = BufferDescriptors;
如果这两个结构以前都不存在,都是新创建的话,我们就需要对其进行初始化配置。
在这里,和本地缓冲区一样我们通过循环遍历来对缓冲区描述器进行初始化。
for (i = 0; i < NBuffers; buf++, i++)
{
CLEAR_BUFFERTAG(buf->tag);
buf->flags = 0;
buf->usage_count = 0;
buf->refcount = 0;
buf->wait_backend_pid = 0;
SpinLockInit(&buf->buf_hdr_lock);
buf->buf_id = i;
这里在缓冲区描述器中存放的buf_id就是非负数,即从0到999共1000个缓
冲区块。
buf->freeNext = i + 1;
buf->io_in_progress_lock = LWLockAssign();
buf->content_lock = LWLockAssign();
因为一开始所有的缓冲区块都是空闲的,所以每个缓冲区块的freeNext指针都
指向它的下一个缓冲区块,而最后一个缓冲区块的freeNext域则为FREENEXT_END_OF_LIST。
}
BufferDescriptors[NBuffers - 1].freeNext = FREENEXT_END_OF_LIST;
}
StrategyInitialize(!foundDescs);
这里调用函数StrategyInitialize来对缓冲区策略进行初始化,跟本地缓冲区不一样,共
享缓冲区有一个专门的数据结构类型BufferStrategyControl对缓冲区的替换进行控制,函数StrategyInitialize就是对这个结构类型进行一些初始化的工作。
}
通过上面的分析我们知道InitBufferPool这个函数主要就是实现了对共享缓冲区部分两个重要数据结构的地址分配缓冲区块和缓冲区描述器,其次就是初始化一个专门用于缓冲区替换策略控制的数据结构。在代码的分析中我们已经提到就是InitBufferPool这个函数并没有给缓冲区块一起分配针对缓冲区块的引用计数,所以下面的函数InitBufferPoolAccess就是实现对引用计数的分配的,至于为什么将这两部分分开放在两个函数中还有待进一步探究。
void
InitBufferPoolAccess(void)
{
PrivateRefCount = (int32 *) calloc(NBuffers, sizeof(int32));
if (!PrivateRefCount)
ereport(FATAL,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("out of memory")));
就是分配一个大小为1000的数组,数组元素类型为整型。
}
文件pgsql/src/backend/storage/buffer/buf_init.c中还有另外一个函数BufferShmemSize,这个函数用来计算缓冲区所需的共享内存的空间大小。
Size
BufferShmemSize(void)
{
Size
size = 0;
size = add_size(size, mul_size(NBuffers, sizeof(BufferDesc)));
size = add_size(size, mul_size(NBuffers, BLCKSZ));
size = add_size(size, StrategyShmemSize());
这里就是分别计算了缓冲区描述器、缓冲区块以及缓冲区策略所占用的内存地址大小,
然后将三者相加之后返回,这里也没有将引用计数的空间大小计算在内,这不得不使我们隐约地感到引用计数是不算在共享缓冲区里面的,事实真相有待进一步揭示。
return size;
}
PostgreSQL中缓冲区管理主要分为共享缓冲区和管理和本地缓冲区管理两大部分,其中共享缓冲区结构以及管理方法主要定义在文件pgsql/src/backend/storage/buffer/buf_init.c,文件pgsql/src/backend/storage/buffer/buf_table.c,文件pgsql/src/backend/storage/buffer/bufmgr.c和文件pgsql/src/backend/storage/buffer/freelist.c中。
我们首先来看文件pgsql/src/backend/storage/buffer/buf_init.c,这个文件主要就是定义了共享缓冲区管理器的初始化方法。在本地缓冲区管理器部分我们知道缓冲区的组织至少包括三个部分:缓冲区块、缓冲区描述器以及引用计数,共享缓冲区也是一样。在文件的开始就定义了这三个全局变量,唯一跟本地缓冲区不同的地方就是,这里定义的变量名前面都没有Local这个字符串,此外这里引用计数的名称前加了一个Private。
BufferDesc *BufferDescriptors;
char *BufferBlocks;
int32
*PrivateRefCount;
然后就是一些其他的用于统计信息的计数器变量:
long int
ReadBufferCount;
long int
ReadLocalBufferCount;
long int
BufferHitCount;
long int
LocalBufferHitCount;
long int
BufferFlushCount;
long int
LocalBufferFlushCount;
long int
BufFileReadCount;
long int
BufFileWriteCount;
其中包括读缓冲区的次数、缓冲区命中的次数、缓冲区文件的读写次数等等,通过这些统计数据我们就可以计算缓冲区命中率一类的更有意义的数据信息。
共享缓冲区因为会涉及到多个事务的并发访问以及日志的记录和检查点的处理,所以相较于本地缓冲区的管理更加复杂,涉及到的数据结构也更加繁琐。在这里,和本地缓冲区一样,我们首先介绍共享缓冲区的初始化。
void
InitBufferPool(void)
{
bool
foundBufs,foundDescs;
BufferDescriptors = (BufferDesc *)
ShmemInitStruct("Buffer Descriptors",
NBuffers
* sizeof(BufferDesc), &foundDescs);
BufferBlocks = (char *)
ShmemInitStruct("Buffer Blocks",
NBuffers
* (Size) BLCKSZ, &foundBufs);
调用函数ShmemInitStruct为缓冲区描述器和缓冲区块分配空间,和本地缓冲区不同的
是这里没有连同私有引用计数一起分配,函数ShmemInitStruct定义在文件pgsql/src/backend/storage/ipc/shmem.c中,它会根据第一个参数,也就是共享内存数据结构的名称来判断这个结构在共享内存中是否已经存在,如果不存在的话就在共享内存段中分配一个这样的结构,我们稍后会对这个重要的函数进行分析。
if (foundDescs || foundBufs)
{
Assert(foundDescs && foundBufs);
}
如果通过调用函数ShmemInitStruct我们发现缓冲区描述器或者缓冲区块已经分配了的
话我们就要验证这两个结构都是已经分配的,因为这两个结构肯定是一起分配的,如果一个还存在另一个已经不存在的话就说明共享内存段出了问题。
else
{
BufferDesc *buf;
int
i;
buf = BufferDescriptors;
如果这两个结构以前都不存在,都是新创建的话,我们就需要对其进行初始化配置。
在这里,和本地缓冲区一样我们通过循环遍历来对缓冲区描述器进行初始化。
for (i = 0; i < NBuffers; buf++, i++)
{
CLEAR_BUFFERTAG(buf->tag);
buf->flags = 0;
buf->usage_count = 0;
buf->refcount = 0;
buf->wait_backend_pid = 0;
SpinLockInit(&buf->buf_hdr_lock);
buf->buf_id = i;
这里在缓冲区描述器中存放的buf_id就是非负数,即从0到999共1000个缓
冲区块。
buf->freeNext = i + 1;
buf->io_in_progress_lock = LWLockAssign();
buf->content_lock = LWLockAssign();
因为一开始所有的缓冲区块都是空闲的,所以每个缓冲区块的freeNext指针都
指向它的下一个缓冲区块,而最后一个缓冲区块的freeNext域则为FREENEXT_END_OF_LIST。
}
BufferDescriptors[NBuffers - 1].freeNext = FREENEXT_END_OF_LIST;
}
StrategyInitialize(!foundDescs);
这里调用函数StrategyInitialize来对缓冲区策略进行初始化,跟本地缓冲区不一样,共
享缓冲区有一个专门的数据结构类型BufferStrategyControl对缓冲区的替换进行控制,函数StrategyInitialize就是对这个结构类型进行一些初始化的工作。
}
通过上面的分析我们知道InitBufferPool这个函数主要就是实现了对共享缓冲区部分两个重要数据结构的地址分配缓冲区块和缓冲区描述器,其次就是初始化一个专门用于缓冲区替换策略控制的数据结构。在代码的分析中我们已经提到就是InitBufferPool这个函数并没有给缓冲区块一起分配针对缓冲区块的引用计数,所以下面的函数InitBufferPoolAccess就是实现对引用计数的分配的,至于为什么将这两部分分开放在两个函数中还有待进一步探究。
void
InitBufferPoolAccess(void)
{
PrivateRefCount = (int32 *) calloc(NBuffers, sizeof(int32));
if (!PrivateRefCount)
ereport(FATAL,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("out of memory")));
就是分配一个大小为1000的数组,数组元素类型为整型。
}
文件pgsql/src/backend/storage/buffer/buf_init.c中还有另外一个函数BufferShmemSize,这个函数用来计算缓冲区所需的共享内存的空间大小。
Size
BufferShmemSize(void)
{
Size
size = 0;
size = add_size(size, mul_size(NBuffers, sizeof(BufferDesc)));
size = add_size(size, mul_size(NBuffers, BLCKSZ));
size = add_size(size, StrategyShmemSize());
这里就是分别计算了缓冲区描述器、缓冲区块以及缓冲区策略所占用的内存地址大小,
然后将三者相加之后返回,这里也没有将引用计数的空间大小计算在内,这不得不使我们隐约地感到引用计数是不算在共享缓冲区里面的,事实真相有待进一步揭示。
return size;
}
相关文章推荐
- 摘自代码阅读方法与实践书籍的知识点总结
- 思途旅游框架部分代码说明总结
- 阅读项目代码,一些总结
- 代码阅读方法(自己总结地 厚厚)
- GIS的学习(二十六)geotools 使用 部分代码总结
- 个人总结 Yii 部分关键代码
- 代码阅读总结之Fitch and Mather 7.0(asp.net发生异常或错误时错误提示页面的处理方法)
- android 音频系统java部分代码阅读
- 阅读笔记:《C#字符串和正则表达式参考手册》 1-4章部分代码
- 代码安全部分总结
- [经验总结]怎样阅读代码?
- 如何让你的阅读更高效?——1700本书阅读总结(部分)
- 总结代码阅读方面的tips
- ANDROID混淆部分代码 分类: Android安装及配置 2014-12-20 09:28 94人阅读 评论(0) 收藏
- 总结redis第二部分(redis常用命令、高级命令特性以及与java代码的结合)
- 中断代码阅读(1)_共享中断和短时间两次同一中断
- 部分代码总结7.29
- [置顶] HM编码器代码阅读(33)——帧间预测的总结
- 代码阅读工具学习总结