已有 1072 次阅读 2010-4-8 05:13 |个人分类:postgresql|系统分类:科研笔记|关键词:PostgresMain,BaseInit,InitProcess
上次写完了Postmaster 里面的一些内存初始化结构,具体参考:
Postmaster的Shared Memory中的shmem index table 内存结构
Postmaster的Shared Memory中的shared buffer pool内存结构
Postmaster的Memory Context 初始化内存结构
PostmasterMain()中的process table的初始化后内存结构
接下来就得写写Postgres进程里面的初始化内存结构了。如何调试PostgresMain()从 pg_usleep(PostAuthDelay * 1000000L);到 for()循环之间的代码请参考:Postmaster 命令 -W 参数使用中的启动参数说明。
我们知道,一个新的connection来到后,postmaster通过fork()一个postgre进程,PostgresMain()开始做一些初始化,然后进入for(;;)循环,处理后续的sql命令。那么从进入PostgresMain()函数开始到for(;;)循环之间关于该进程有哪些重要的初始化呢?因为我们想知道是否在内存中是否通过初始化给将要buffer的table,heaptuple预留出相应的内存空间。
通过分析,在PostgresMain()中我们发现有三个重要的初始化函数:
BaseInit();
InitProcess();
am_superuser = InitPostgres(dbname, InvalidOid, username, NULL);
1.我们先来看BaseInit();
void BaseInit(void){
/*
* Attach to shared memory and semaphores, and initialize our
* input/output/debugging file descriptors.
*/
InitCommunication();
DebugFileOpen();
/* Do local initialization of file, storage and buffer managers */
InitFileAccess();
smgrinit();
InitBufferPoolAccess();
}
其中InitCommunication()没做什么事。DebugFileOpen()暂时我们先不管它。接下来就是InitFileAccess():
void InitFileAccess(void){
Assert(SizeVfdCache == 0); /* call me only once */
/* initialize cache header entry */
VfdCache = (Vfd *) malloc(sizeof(Vfd));
if (VfdCache == NULL)
ereport(FATAL,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg(“out of memory”)));
MemSet((char *) &(VfdCache[0]), 0, sizeof(Vfd));
VfdCache->fd = VFD_CLOSED;
SizeVfdCache = 1;
/* register proc-exit hook to ensure temp files are dropped at exit */
on_proc_exit(AtProcExit_Files, 0);
}
我们发现其实也没做什么工作,主要是初始化了VfdCache和SizeVfdCache。
接下来是 smgrinit(),即初始化硬盘管理器,static const f_smgr smgrsw[],通过跟踪我们发现同样没做什么工作。
那么最后一项InitBufferPoolAccess()做了哪些工作呢?通过该调用:
void InitBufferPoolAccess(void){
/*
* Allocate and zero local arrays of per-buffer info.
*/
PrivateRefCount = (int32 *) calloc(NBuffers, sizeof(int32));
if (!PrivateRefCount)
ereport(FATAL,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg(“out of memory”)));
}
我们发现,只是初始化了NBuffers个PrivateRefCount 。
故,我们小结一下:BaseInit();简单初始化了几个变量:VfdCache ,SizeVfdCache ,PrivateRefCount 。
如下图:
2.接下来我们看看InitProcess()
void InitProcess(void){
volatile PROC_HDR *procglobal = ProcGlobal;
int i;
if (procglobal == NULL) elog(PANIC, “proc header uninitialized”);
if (MyProc != NULL) elog(ERROR, “you already exist”);
SpinLockAcquire(ProcStructLock);
set_spins_per_delay(procglobal->spins_per_delay);
if (IsAutoVacuumWorkerProcess())
MyProc = procglobal->autovacFreeProcs;
else
MyProc = procglobal->freeProcs;
if (MyProc != NULL) {
if (IsAutoVacuumWorkerProcess())
procglobal->autovacFreeProcs = (PGPROC *) MyProc->links.next;
else
procglobal->freeProcs = (PGPROC *) MyProc->links.next;
SpinLockRelease(ProcStructLock);
}
else {
SpinLockRelease(ProcStructLock);
ereport(FATAL,
(errcode(ERRCODE_TOO_MANY_CONNECTIONS),
errmsg(“sorry, too many clients already”)));
}
if (IsUnderPostmaster)
MarkPostmasterChildActive();
SHMQueueElemInit(&(MyProc->links));
MyProc->waitStatus = STATUS_OK;
MyProc->lxid = InvalidLocalTransactionId;
MyProc->xid = InvalidTransactionId;
MyProc->xmin = InvalidTransactionId;
MyProc->pid = MyProcPid;
/* backendId, databaseId and roleId will be filled in later */
MyProc->backendId = InvalidBackendId;
MyProc->databaseId = InvalidOid;
MyProc->roleId = InvalidOid;
MyProc->inCommit = false;
MyProc->vacuumFlags = 0;
if (IsAutoVacuumWorkerProcess())
MyProc->vacuumFlags |= PROC_IS_AUTOVACUUM;
MyProc->lwWaiting = false;
MyProc->lwExclusive = false;
MyProc->lwWaitLink = NULL;
MyProc->waitLock = NULL;
MyProc->waitProcLock = NULL;
for (i = 0; i < NUM_LOCK_PARTITIONS; i++)
SHMQueueInit(&(MyProc->myProcLocks[i]));
PGSemaphoreReset(&MyProc->sem);
on_shmem_exit(ProcKill, 0);
InitDeadLockChecking();
}
可见,MyProc主要从ProcGlobal->freeProcs列表中找到第一个,赋给自己,然后简单的填充各项数据。
这样,经过第2项的初始化之后,我们得出内存中的数据结构为下图,图中灰色颜色为在heap中,浅黄色表示在shared memory中,红色代表在data段:
3.am_superuser = InitPostgres(dbname, InvalidOid, username, NULL)
进入最复杂的InitPostgres(“mydb”, InvalidOid, “postgres”, NULL)。我们先列列都调用了哪些重要函数:
SetDatabasePath(fullpath);
InitProcessPhase2();
InitBufferPoolBackend();
RelationCacheInitialize();
InitCatalogCache();
InitPlanCache();
EnablePortalManager();
RelationCacheInitializePhase2();
CheckMyDatabase(dbname, am_superuser);
InitializeSearchPath();
InitializeClientEncoding();
哇哈哈,可真不少,不过一步一步走,总会走完长征路的,先泡杯竹叶青,慢慢品,抗一下辐射,因为下面的路很长、很长。我先把重要的两个函数用下划线标出来,让你先知道哪个是重点。首先声明,看完这部分是有好处的,就是你很快就会发现你能解读生成QueryTree过程中的所有相关代码了。绝不是“辛辛苦苦几十年,一夜回到解放前”。
好,开工,先来看:SetDatabasePath(fullpath);
这个最简单,就是调用 DatabasePath = strdup(path); 设置 DatabasePath 全局变量,为:”base/16384″,这里的16384是Oid MyDatabaseId =16384。怎么知道MyDatabaseId 的呢,实际上实现很简单,就是直接读取文件/usr/local/pgsql/data/global/pg_database 把有”mydb”那一行找出来,解析一下就是了。我的pg_database示例如下:
“template1” 1 1663 648
“template0” 11563 1663 648
“postgres” 11564 1663 648
“mydb” 16384 1663 648
“test3” 17240 1663 648
接下来是InitProcessPhase2(); 还记得第二步当中的InitProcess()吗?那里几乎只是简单的初始化了一下,这里要继续初始化什么呢?先看调用这个函数的注释:
/*
* Finish filling in the PGPROC struct, and add it to the ProcArray. (We
* need to know MyDatabaseId before we can do this, since it’s entered
* into the PGPROC struct.)
*
* Once I have done this, I am visible to other backends!
*/
看样子也没干什么大不了的事,进到函数里面看看,果然如此:
先是MyProc->databaseId = MyDatabaseId; 赋值16384。
接着ProcArrayAdd(MyProc);。里面几乎也没干什么事,只是设置了全局变量procArray->procs[arrayP->numProcs] = proc;
到这里我们画张图看看,内存里都有了啥:
看样子上面没干什么复杂的东西,接下来分析分析InitBufferPoolBackend();
这里很简单就是注册了一个回调函数 on_shmem_exit(AtProcExit_Buffers, 0);
接下来就是 RelationCacheInitialize();
该函数只是在heap上创建了一个用于relation查找的hash表。我们在postmaster的初始化里对hash表数据结构已经很熟悉了,只是这里创建在heap上,而不是shared memory里,该RealtionIdCache内存结构如下图。
一点说明:有些人会问,这个RealtionIdCache是干什么用的呢?试想一下,我们数据库中有很多relation,这些relation如果要从硬盘上搬到内存的话,肯定是在内存中要有个地方放该relation的实际数据的,例如可能访问该relation的第一个page,放这个实际数据的位置就是postmaster中初始化的BLOCK数据结构,那么肯定有一个数据结构来描述这个打开的relation的,这个数据结构是什么呢,就是Relation,那么如果打开了很多个Relation,如何管理呢,这就是RealtionIdCache要干的事。
注意这里的400是默认的hash表容量,即最大打开的relation个数。
接下来就是通过InitCatalogCache();初始化syscache 。
void InitCatalogCache(void){
int cacheId;
Assert(!CacheInitialized);
MemSet(SysCache, 0, sizeof(SysCache));
for (cacheId = 0; cacheId < SysCacheSize; cacheId++) {
SysCache[cacheId] = InitCatCache(cacheId,
cacheinfo[cacheId].reloid,
cacheinfo[cacheId].indoid,
cacheinfo[cacheId].reloidattr,
cacheinfo[cacheId].nkeys,
cacheinfo[cacheId].key,
cacheinfo[cacheId].nbuckets);
if (!PointerIsValid(SysCache[cacheId]))
elog(ERROR, “could not initialize cache %u (%d)”,
cacheinfo[cacheId].reloid, cacheId);
}
CacheInitialized = true;
}
从上面代码知道,用cacheinfo硬编码在源代码中的数据初始化static CatCache *SysCache[lengthof(cacheinfo)]; 这里lengthof(cacheinfo)] = 54;
下图更加清晰一些,注意图中故意把两个将要用到的CatCache 里面的数据列了一下:
这两个CatCache 分别是
static CatCache *SysCache[19] cc_reloid=1262
我们用mydb=# select oid,relname from pg_class where oid = 1262;一查
oid | relname
——+————-
1262 | pg_database
(1 row)
便知道 表明该项是关于pg_database 这个Relation的。
static CatCache *SysCache[37] cc_reloid=1259
我们用mydb=# select oid,relname from pg_class where oid=1259 ;一查
oid | relname
——+———-
1259 | pg_class
(1 row)
便知道 表明该项是关于pg_class 这个Relation的。
在debug窗口看到的CatCache[37]为:
“SysCache[37]” = 0x09bf5e94
id = 37
cc_next = 0x09bf1d64
cc_relname = 0x08488751 “(not known yet)”
*cc_relname = ‘(‘
cc_reloid = 1259
cc_indexoid = 2663
cc_relisshared = false
cc_tupdesc = 0x00000000
cc_reloidattr = -2
cc_ntup = 0
cc_nbuckets = 1024
cc_nkeys = 2
cc_key = 0x09bf5ec0
cc_key[0] = 1
cc_key[1] = 2
cc_key[2] = 0
cc_key[3] = 0
cc_hashfunc = 0x09bf5ed0
cc_hashfunc[0] = 0x00000000
cc_hashfunc[1] = 0x00000000
cc_hashfunc[2] = 0x00000000
cc_hashfunc[3] = 0x00000000
cc_skey = 0x09bf5ee0
cc_skey[0] = {…}
sk_flags = 0
sk_attno = 0
sk_strategy = 0
sk_subtype = 0
sk_func = {…}
fn_addr = 0x00000000
fn_oid = 0
fn_nargs = 0
fn_strict = false
fn_retset = false
fn_stats = 0
fn_extra = 0x00000000
fn_mcxt = 0x00000000
fn_expr = 0x00000000
sk_argument = 0
cc_skey[1] = {…}
sk_flags = 0
sk_attno = 0
sk_strategy = 0
sk_subtype = 0
sk_func = {…}
fn_addr = 0x00000000
fn_oid = 0
fn_nargs = 0
fn_strict = false
fn_retset = false
fn_stats = 0
fn_extra = 0x00000000
fn_mcxt = 0x00000000
fn_expr = 0x00000000
sk_argument = 0
cc_skey[2] = {…}
sk_flags = 0
sk_attno = 0
sk_strategy = 0
sk_subtype = 0
sk_func = {…}
fn_addr = 0x00000000
fn_oid = 0
fn_nargs = 0
fn_strict = false
fn_retset = false
fn_stats = 0
fn_extra = 0x00000000
fn_mcxt = 0x00000000
fn_expr = 0x00000000
sk_argument = 0
cc_skey[3] = {…}
sk_flags = 0
sk_attno = 0
sk_strategy = 0
sk_subtype = 0
sk_func = {…}
fn_addr = 0x00000000
fn_oid = 0
fn_nargs = 0
fn_strict = false
fn_retset = false
fn_stats = 0
fn_extra = 0x00000000
fn_mcxt = 0x00000000
fn_expr = 0x00000000
sk_argument = 0
cc_isname = 0x09bf5f90
cc_isname[0] = false
cc_isname[1] = false
cc_isname[2] = false
cc_isname[3] = false
cc_lists = {…}
dll_head = 0x00000000
dll_tail = 0x00000000
cc_bucket = 0x09bf5f9c
cc_bucket[0] = {…}
dll_head = 0x00000000
dll_tail = 0x00000000
和我们图中示意完全一样。
下面是全部的54个系统CatCache信息,我用设置CACHEDEBUG然后 log如下:
DEBUG: InitCatCache: rel=2600 ind=2650 id=0 nkeys=1 size=32
DEBUG: InitCatCache: rel=2601 ind=2651 id=1 nkeys=1 size=4
DEBUG: InitCatCache: rel=2601 ind=2652 id=2 nkeys=1 size=4
DEBUG: InitCatCache: rel=2602 ind=2654 id=3 nkeys=2 size=64
DEBUG: InitCatCache: rel=2602 ind=2653 id=4 nkeys=4 size=64
DEBUG: InitCatCache: rel=2603 ind=2655 id=5 nkeys=4 size=64
DEBUG: InitCatCache: rel=1249 ind=2658 id=6 nkeys=2 size=2048
DEBUG: InitCatCache: rel=1249 ind=2659 id=7 nkeys=2 size=2048
DEBUG: InitCatCache: rel=1261 ind=2695 id=8 nkeys=2 size=128
DEBUG: InitCatCache: rel=1261 ind=2694 id=9 nkeys=2 size=128
DEBUG: InitCatCache: rel=1260 ind=2676 id=10 nkeys=1 size=128
DEBUG: InitCatCache: rel=1260 ind=2677 id=11 nkeys=1 size=128
DEBUG: InitCatCache: rel=2605 ind=2661 id=12 nkeys=2 size=256
DEBUG: InitCatCache: rel=2616 ind=2686 id=13 nkeys=3 size=64
DEBUG: InitCatCache: rel=2616 ind=2687 id=14 nkeys=1 size=64
DEBUG: InitCatCache: rel=2607 ind=2668 id=15 nkeys=4 size=128
DEBUG: InitCatCache: rel=2607 ind=2669 id=16 nkeys=2 size=128
DEBUG: InitCatCache: rel=2606 ind=2667 id=17 nkeys=1 size=1024
DEBUG: InitCatCache: rel=2607 ind=2670 id=18 nkeys=1 size=128
DEBUG: InitCatCache: rel=1262 ind=2672 id=19 nkeys=1 size=4
DEBUG: InitCatCache: rel=3501 ind=3502 id=20 nkeys=1 size=256
DEBUG: InitCatCache: rel=3501 ind=3503 id=21 nkeys=2 size=256
DEBUG: InitCatCache: rel=2328 ind=548 id=22 nkeys=1 size=8
DEBUG: InitCatCache: rel=2328 ind=112 id=23 nkeys=1 size=8
DEBUG: InitCatCache: rel=1417 ind=549 id=24 nkeys=1 size=32
DEBUG: InitCatCache: rel=1417 ind=113 id=25 nkeys=1 size=32
DEBUG: InitCatCache: rel=2610 ind=2679 id=26 nkeys=1 size=1024
DEBUG: InitCatCache: rel=2612 ind=2681 id=27 nkeys=1 size=4
DEBUG: InitCatCache: rel=2612 ind=2682 id=28 nkeys=1 size=4
DEBUG: InitCatCache: rel=2615 ind=2684 id=29 nkeys=1 size=256
DEBUG: InitCatCache: rel=2615 ind=2685 id=30 nkeys=1 size=256
DEBUG: InitCatCache: rel=2617 ind=2689 id=31 nkeys=4 size=1024
DEBUG: InitCatCache: rel=2617 ind=2688 id=32 nkeys=1 size=1024
DEBUG: InitCatCache: rel=2753 ind=2754 id=33 nkeys=3 size=64
DEBUG: InitCatCache: rel=2753 ind=2755 id=34 nkeys=1 size=64
DEBUG: InitCatCache: rel=1255 ind=2691 id=35 nkeys=3 size=2048
DEBUG: InitCatCache: rel=1255 ind=2690 id=36 nkeys=1 size=2048
DEBUG: InitCatCache: rel=1259 ind=2663 id=37 nkeys=2 size=1024
DEBUG: InitCatCache: rel=1259 ind=2662 id=38 nkeys=1 size=1024
DEBUG: InitCatCache: rel=2618 ind=2693 id=39 nkeys=2 size=1024
DEBUG: InitCatCache: rel=2619 ind=2696 id=40 nkeys=2 size=1024
DEBUG: InitCatCache: rel=3603 ind=3609 id=41 nkeys=3 size=4
DEBUG: InitCatCache: rel=3602 ind=3608 id=42 nkeys=2 size=16
DEBUG: InitCatCache: rel=3602 ind=3712 id=43 nkeys=1 size=16
DEBUG: InitCatCache: rel=3600 ind=3604 id=44 nkeys=2 size=16
DEBUG: InitCatCache: rel=3600 ind=3605 id=45 nkeys=1 size=16
DEBUG: InitCatCache: rel=3601 ind=3606 id=46 nkeys=2 size=4
DEBUG: InitCatCache: rel=3601 ind=3607 id=47 nkeys=1 size=4
DEBUG: InitCatCache: rel=3764 ind=3766 id=48 nkeys=2 size=16
DEBUG: InitCatCache: rel=3764 ind=3767 id=49 nkeys=1 size=16
DEBUG: InitCatCache: rel=1247 ind=2704 id=50 nkeys=2 size=1024
DEBUG: InitCatCache: rel=1247 ind=2703 id=51 nkeys=1 size=1024
DEBUG: InitCatCache: rel=1418 ind=174 id=52 nkeys=1 size=128
DEBUG: InitCatCache: rel=1418 ind=175 id=53 nkeys=2 size=128
接下来我们分析分析InitPlanCache();
/*
* InitPlanCache: initialize module during InitPostgres.
*
* All we need to do is hook into inval.c’s callback lists.
*/
void InitPlanCache(void){
CacheRegisterRelcacheCallback(PlanCacheRelCallback, (Datum) 0);
CacheRegisterSyscacheCallback(PROCOID, PlanCacheFuncCallback, (Datum) 0);
CacheRegisterSyscacheCallback(NAMESPACEOID, PlanCacheSysCallback, (Datum) 0);
CacheRegisterSyscacheCallback(OPEROID, PlanCacheSysCallback, (Datum) 0);
CacheRegisterSyscacheCallback(AMOPOPID, PlanCacheSysCallback, (Datum) 0);
}
正如源码注释中所说的那样,该函数只是注册了5个callback 函数。
接下来是 EnablePortalManager();
/*
* EnablePortalManager
* Enables the portal management module at backend startup.
*/
void EnablePortalManager(void){
HASHCTL ctl;
Assert(PortalMemory == NULL);
PortalMemory = AllocSetContextCreate(TopMemoryContext,
“PortalMemory”,
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
ctl.keysize = MAX_PORTALNAME_LEN;
ctl.entrysize = sizeof(PortalHashEnt);
/*
* use PORTALS_PER_USER as a guess of how many hash table entries to
* create, initially
*/
PortalHashTable = hash_create(“Portal hash”, PORTALS_PER_USER,&ctl, HASH_ELEM);
}
我们发现该函数仅仅是做了又创建了一个hash表,so simple的事情。该hash表早就是我们的老东家了,此处不再上图。注意PORTALS_PER_USER =16。
接下来是一直让我们做噩梦的RelationCacheInitializePhase2();现在我们要看看他老人家有多恐怖。我们先列列有哪些函数调用:
load_relcache_init_file()
formrdesc(“pg_class”, PG_CLASS_RELTYPE_OID,
true, Natts_pg_class, Desc_pg_class);
formrdesc(“pg_attribute”, PG_ATTRIBUTE_RELTYPE_OID,
false, Natts_pg_attribute, Desc_pg_attribute);
formrdesc(“pg_proc”, PG_PROC_RELTYPE_OID,
true, Natts_pg_proc, Desc_pg_proc);
formrdesc(“pg_type”, PG_TYPE_RELTYPE_OID,
true, Natts_pg_type, Desc_pg_type);
load_critical_index(ClassOidIndexId,
RelationRelationId);
load_critical_index(AttributeRelidNumIndexId,
AttributeRelationId);
load_critical_index(IndexRelidIndexId,
IndexRelationId);
load_critical_index(OpclassOidIndexId,
OperatorClassRelationId);
load_critical_index(AccessMethodStrategyIndexId,
AccessMethodOperatorRelationId);
load_critical_index(AccessMethodProcedureIndexId,
AccessMethodProcedureRelationId);
load_critical_index(OperatorOidIndexId,
OperatorRelationId);
load_critical_index(RewriteRelRulenameIndexId,
RewriteRelationId);
load_critical_index(TriggerRelidNameIndexId,
TriggerRelationId);
hash_seq_init(&status, RelationIdCache);
while ((idhentry = (RelIdCacheEnt *) hash_seq_search(&status)) != NULL){}
架势不小呀,我们一个一个来攻克。
九阴真经之load_relcache_init_file()
先看看注释:
* + When the backend is started up in normal mode, we load an image
* of the appropriate relation descriptors, in internal format,
* from an initialization file in the data/base/… directory.
*
* + If the initialization file isn’t there, then we create the
* relation descriptors using sequential scans and write ’em to
* the initialization file for use by subsequent backends.
首先该文件为char initfilename =”base/16384/pg_internal.init”。上面的注释说明,如果不存在该文件,则顺序扫描并创建relation descriptors,之后写入该文件。如果该文件存在,则简单的load进内存,填充 relation descriptors。
A.先假定第一次,我们没有base/16384/pg_internal.init 该文件。则该load_relcache_init_file()仅仅是返回false。不做任何工作,进而进入
formrdesc(“pg_class”, PG_CLASS_RELTYPE_OID,
true, Natts_pg_class, Desc_pg_class);
formrdesc(“pg_attribute”, PG_ATTRIBUTE_RELTYPE_OID,
false, Natts_pg_attribute, Desc_pg_attribute);
formrdesc(“pg_proc”, PG_PROC_RELTYPE_OID,
true, Natts_pg_proc, Desc_pg_proc);
formrdesc(“pg_type”, PG_TYPE_RELTYPE_OID,
true, Natts_pg_type, Desc_pg_type);
阶段。并接下来进入:
load_critical_index(ClassOidIndexId,
RelationRelationId);
load_critical_index(AttributeRelidNumIndexId,
AttributeRelationId);
load_critical_index(IndexRelidIndexId,
IndexRelationId);
load_critical_index(OpclassOidIndexId,
OperatorClassRelationId);
load_critical_index(AccessMethodStrategyIndexId,
AccessMethodOperatorRelationId);
load_critical_index(AccessMethodProcedureIndexId,
AccessMethodProcedureRelationId);
load_critical_index(OperatorOidIndexId,
OperatorRelationId);
load_critical_index(RewriteRelRulenameIndexId,
RewriteRelationId);
load_critical_index(TriggerRelidNameIndexId,
TriggerRelationId);
阶段。然后是
hash_seq_init(&status, RelationIdCache);
while ((idhentry = (RelIdCacheEnt *) hash_seq_search(&status)) != NULL)
阶段。
最后是如果没有”base/16384/pg_internal.init”文件,则写入到base/16384/pg_internal.init 文件中去。
我们先来看看第一步:
formrdesc(“pg_class”, PG_CLASS_RELTYPE_OID,
true, Natts_pg_class, Desc_pg_class);
我们先看看这个函数的原型:
static void formrdesc
(const char *relationName, Oid relationReltype,bool hasoids, int natts, FormData_pg_attribute *att){}
那么传入的参数为:
formrdesc(“pg_class”, 83, true, 25, Desc_pg_class);
那么Desc_pg_class是什么呢,我们来看看定义:
static FormData_pg_attribute Desc_pg_class[Natts_pg_class] = {Schema_pg_class};
而Schema_pg_class又定义为:
/* —————-
* pg_class
* —————-
*/
#define Schema_pg_class \
{ 1259, {“relname”}, 19, -1, NAMEDATALEN, 1, 0, -1, -1, false, ‘p’, ‘c’, true, false, false, true, 0, { 0 } }, \
{ 1259, {“relnamespace”}, 26, -1, 4, 2, 0, -1, -1, true, ‘p’, ‘i’, true, false, false, true, 0, { 0 } }, \
{ 1259, {“reltype”}, 26, -1, 4, 3, 0, -1, -1, true, ‘p’, ‘i’, true, false, false, true, 0, { 0 } }, \
{ 1259, {“relowner”}, 26, -1, 4, 4, 0, -1, -1, true, ‘p’, ‘i’, true, false, false, true, 0, { 0 } }, \
{ 1259, {“relam”}, 26, -1, 4, 5, 0, -1, -1, true, ‘p’, ‘i’, true, false, false, true, 0, { 0 } }, \
{ 1259, {“relfilenode”}, 26, -1, 4, 6, 0, -1, -1, true, ‘p’, ‘i’, true, false, false, true, 0, { 0 } }, \
{ 1259, {“reltablespace”}, 26, -1, 4, 7, 0, -1, -1, true, ‘p’, ‘i’, true, false, false, true, 0, { 0 } }, \
{ 1259, {“relpages”}, 23, -1, 4, 8, 0, -1, -1, true, ‘p’, ‘i’, true, false, false, true, 0, { 0 } }, \
{ 1259, {“reltuples”}, 700, -1, 4, 9, 0, -1, -1, FLOAT4PASSBYVAL, ‘p’, ‘i’, true, false, false, true, 0, { 0 } }, \
{ 1259, {“reltoastrelid”}, 26, -1, 4, 10, 0, -1, -1, true, ‘p’, ‘i’, true, false, false, true, 0, { 0 } }, \
{ 1259, {“reltoastidxid”}, 26, -1, 4, 11, 0, -1, -1, true, ‘p’, ‘i’, true, false, false, true, 0, { 0 } }, \
{ 1259, {“relhasindex”}, 16, -1, 1, 12, 0, -1, -1, true, ‘p’, ‘c’, true, false, false, true, 0, { 0 } }, \
{ 1259, {“relisshared”}, 16, -1, 1, 13, 0, -1, -1, true, ‘p’, ‘c’, true, false, false, true, 0, { 0 } }, \
{ 1259, {“relistemp”}, 16, -1, 1, 14, 0, -1, -1, true, ‘p’, ‘c’, true, false, false, true, 0, { 0 } }, \
{ 1259, {“relkind”}, 18, -1, 1, 15, 0, -1, -1, true, ‘p’, ‘c’, true, false, false, true, 0, { 0 } }, \
{ 1259, {“relnatts”}, 21, -1, 2, 16, 0, -1, -1, true, ‘p’, ‘s’, true, false, false, true, 0, { 0 } }, \
{ 1259, {“relchecks”}, 21, -1, 2, 17, 0, -1, -1, true, ‘p’, ‘s’, true, false, false, true, 0, { 0 } }, \
{ 1259, {“relhasoids”}, 16, -1, 1, 18, 0, -1, -1, true, ‘p’, ‘c’, true, false, false, true, 0, { 0 } }, \
{ 1259, {“relhaspkey”}, 16, -1, 1, 19, 0, -1, -1, true, ‘p’, ‘c’, true, false, false, true, 0, { 0 } }, \
{ 1259, {“relhasrules”}, 16, -1, 1, 20, 0, -1, -1, true, ‘p’, ‘c’, true, false, false, true, 0, { 0 } }, \
{ 1259, {“relhastriggers”},16, -1, 1, 21, 0, -1, -1, true, ‘p’, ‘c’, true, false, false, true, 0, { 0 } }, \
{ 1259, {“relhassubclass”},16, -1, 1, 22, 0, -1, -1, true, ‘p’, ‘c’, true, false, false, true, 0, { 0 } }, \
{ 1259, {“relfrozenxid”}, 28, -1, 4, 23, 0, -1, -1, true, ‘p’, ‘i’, true, false, false, true, 0, { 0 } }, \
{ 1259, {“relacl”}, 1034, -1, -1, 24, 1, -1, -1, false, ‘x’, ‘i’, false, false, false, true, 0, { 0 } }, \
{ 1259, {“reloptions”}, 1009, -1, -1, 25, 1, -1, -1, false, ‘x’, ‘i’, false, false, false, true, 0, { 0 } }
可见Desc_pg_class主要是定义了pg_class表结构里的25个字段相关的信息。
那么回头我们看看formrdesc()函数里主要干了什么事情,经过一分析,我们知道
Relation relation;
relation = (Relation) palloc0(sizeof(RelationData));
……./*填充数据*/
RelationCacheInsert(relation); /*在上面刚刚创建的RelationIdCache哈希表中登记一下*/
这样子我们可以画个图,看看这个Relation到底是什么。图中只画出RelationIdCache里的hash表项,hash值我直接省略。
那么上面的图是否正确呢?我们debug一下,看看这个relation到底什么样。
relation 0x09812f90
rd_node {…}
spcNode 1663
dbNode 16384
relNode 1259
rd_smgr 0x00000000
rd_targblock 4294967295
rd_refcnt 1
rd_istemp false
rd_islocaltemp false
rd_isnailed true
rd_isvalid false
rd_indexvalid 0
rd_createSubid 0
rd_newRelfilenodeSubid 0
rd_rel 0x0981b040
relname {…}
data 0x0981b040
relnamespace 11
reltype 83
relowner 0
relam 0
relfilenode 1259
reltablespace 0
relpages 1
reltuples 1.0
reltoastrelid 0
reltoastidxid 0
relhasindex true
relisshared false
relistemp false
relkind ‘r’
relnatts 25
relchecks 0
relhasoids true
relhaspkey false
relhasrules false
relhastriggers false
relhassubclass false
relfrozenxid 0
relacl 0x0981b0bc
relacl[0] 126
reloptions 0x0981b0c0
rd_att 0x09838780
natts 25
attrs 0x0983879c
attrs[0] 0x09838800
attrelid 1259
attname {…}
data 0x09838804
data[0] ‘r’
data[1] ‘e’
data[2] ‘l’
data[3] ‘n’
data[4] ‘a’
data[5] ‘m’
data[6] ‘e’
data[7] 0
data[8] 0
data[9] 0
…
data[63]
atttypid 19
attstattarget -1
attlen 64
attnum 1
attndims 0
attcacheoff 0
atttypmod -1
attbyval false
attstorage ‘p’
attalign ‘c’
attnotnull true
atthasdef false
attisdropped false
attislocal true
attinhcount 0
attacl 0x09838868
attrs[1] 0x09838868
attrs[2] 0x098388d0
attrs[3] 0x09838938
attrs[4] 0x098389a0
….
attrs[23] 0x09839158
attrs[24] 0x098391c0
constr 0x09806e88
tdtypeid 83
tdtypmod -1
tdhasoid true
tdrefcount 1
rd_id 1259
rd_indexlist 0x00000000
rd_indexattr 0x00000000
rd_oidindex 0
rd_lockInfo {…}
lockRelId {…}
relId 1259
dbId 16384
rd_rules 0x00000000
rd_rulescxt 0x00000000
trigdesc 0x00000000
rd_options 0x00000000
rd_index 0x00000000
rd_indextuple 0x00000000
rd_am 0x00000000
rd_indexcxt 0x00000000
rd_aminfo 0x00000000
rd_opfamily 0x00000000
rd_opcintype 0x00000000
rd_operator 0x00000000
rd_support 0x00000000
rd_supportinfo 0x00000000
rd_indoption 0x00000000
rd_indexprs 0x00000000
rd_indpred 0x00000000
rd_amcache 0x00000000
rd_fsm_nblocks 4294967295
rd_vm_nblocks 4294967295
pgstat_info 0x00000000
可见完全正确。
剩余的其他三项: formrdesc(“pg_attribute”, PG_ATTRIBUTE_RELTYPE_OID,
false, Natts_pg_attribute, Desc_pg_attribute);
formrdesc(“pg_proc”, PG_PROC_RELTYPE_OID,
true, Natts_pg_proc, Desc_pg_proc);
formrdesc(“pg_type”, PG_TYPE_RELTYPE_OID,
true, Natts_pg_type, Desc_pg_type);
分析就类似了,不再详述。
我们先来看看第二步:
load_critical_index(ClassOidIndexId,
RelationRelationId);
看看这里面都初始化了哪些内容。
上式实际上是调用static void load_critical_index(2662, 1259)。在该函数中实际上只调用了:
Relation ird;
ird = RelationBuildDesc(2662, true);
我们看看RelationBuildDesc()有什么?
pg_class_tuple = ScanPgRelation(targetRelId, true); 其中targetRelId=2662
relid = HeapTupleGetOid(pg_class_tuple);
relp = (Form_pg_class) GETSTRUCT(pg_class_tuple);
relation = AllocateRelationDesc(relp);
…
RelationBuildTupleDesc(relation);
….
我们发现纵观全文从开始到现在,唯独这个ScanPgRelation(targetRelId, true)最复杂,具体细节我不在展开,这里我只是说明该函数的作用就是首先把该pg_class 的6个page中的第一个读入到内存,顺序抽取各个tuple,比较oid是否等于2662,若相等,则返回,若不相等,则读入第2个page,用于描述该过程的数据结构是MdfdVec->mdfd_chain。
(Form_pg_class) GETSTRUCT(pg_class_tuple);的作用就是把该tupe实际指的数据抽取填充新建的relation的rd_rel指向的Form_pg_class数据结构。
RelationBuildTupleDesc(relation);主要是填充一些默认数值。这样我们得出如下几张图,图中绘出了用到的数据结构,以及新生成的oid为2662 index的relation。
下图是在这个阶段中对pg_class的relation数据结构的修改:
下图是这阶段搜索tuple用到的数据结构:
下图是新生成的oid为2662的新的relation数据结构:
剩余的load index 类似,这里不再展开描述。
接下来我们进入第三个阶段:
hash_seq_init(&status, RelationIdCache); 很简单,只是初始化了几个变量
while ((idhentry = (RelIdCacheEnt *) hash_seq_search(&status)) != NULL){}
所以我们来看看while循环。
其实这里面非常简单,就是顺序扫描RelationIdCache hash表,找到每一项RelIdCacheEnt ,继而找到该relation,如果该relation->rd_rel->relhasrules 和relhastriggers为true,则导入相应的rules和triggers,我们这里就暂时不分析如何导入这些rules和triggers了。
接下来我们进入第四个阶段:把相应的RelationIdCache 内容写到base/16384/pg_internal.init 文件中去。
由于涉及的内容不复杂,这一步我们不分析了。
B.如果我们已经有了base/16384/pg_internal.init 该文件,则load_relcache_init_file()的代码用于导入到该映像文件到内存中去了。
由于内容对我们理解后续的步骤不是很重要,暂缓分析。
至此,我们的对RelationCacheInitializePhase2()的分析告一段落,这里做个小结,这个阶段主要是在RelationIdCache 中生成几个系统表和index在内存中的relation表示。由此可见relation是在内存中的核心表示。
接下来是CheckMyDatabase(dbname, am_superuser);
这个阶段做的工作主要是扫描SysCache[54]中的DATABASEOID项,如果TupleDesc cc_tupdesc=NULL,则创建该relation并把该relation的TupleDesc rd_att属性赋给cc_tupdesc,并打开pg_database relation,顺序读取其中oid为dbname的中的tuple,并把该tuple的encoding等信息,赋给DatabaseEncoding等全局变量。
所以该阶段做了两个事情,一个是创建oid=DATABASEOID的relation内存数据结构,然后顺序读取该relation中的各个tuple,找到oid=dbname的tuple,读取各个参数,赋给全局变量以及更新SysCache中的相应项。
接下来是InitializeSearchPath();
注册了一个回调函数,并设置
baseSearchPathValid = false;
接下来是InitializeClientEncoding();
实际上很简单,就是设置static pg_enc2name *clientEncoding =PG_UTF8
至此,全部结束。
本文引用地址:http://blog.sciencenet.cn/home.php?mod=space&uid=419883&do=blog&id=309933