上节分析到从汇编代码正式跳转到C++代码,即kerne/Main.cpp中的kmain函数,接下来分析相关类构造函数以及方法函数的初始化过程。
kernel/Main.cpp文件代码如下:
#include <FreeNOS/Kernel.h>
#include <FreeNOS/Init.h>
#include <FreeNOS/Scheduler.h>
void kmain()
{
/* Initialize kernel. */
INITRUN(&initStart, &initEnd);
/* Start scheduling. */
scheduler->executeNext();
}
INITRUN(&initStart, &initEnd);部分是实现相关类构造函数以及方法函数的初始化过程的关键部分,展开该宏函数如下:
/** A memory address. */
typedef unsigned long Address;
/** Start of initialization routines. */
extern Address initStart;
/** Marks the end of all initialization functions. */
extern Address initEnd;
#define INITRUN(initStart,initEnd) \
{ \
Address *i; \
\
for (i = (initStart); i < (initEnd); i++) \
{ \
(*(InitHandler **) i)(); \
} \
}
可以到分别调用地址处于initStart与initEnd的函数,initEnd是起始地址,initEnd是结束地址,那么函数地址处于中间段是怎么被放置二者之间呢?这个就又要看看kernel.ld文件的内容了:
可以看到initStart与initEnd之间的部分是处于目标文件的.init* 段,而且是排序存放的。那么在目录查找.init符号:
查看inlude/Init.h文件:
展开宏定义:
attribute((section(s))):需要了解一下gcc的__attribute__的编绎属性,__attribute__主要用于改变所声明或定义的函数或 数据的特性,它有很多子项,用于改变作用对象的特性。对代码段起作用子项section:
__attribute__的section子项的使用格式为:
attribute((section(“section_name”)))
其作用是将作用的函数或数据放入指定名为"section_name"输入段。
最后都是定义一个静态函数指针,不过一个是以类命名的的函数指正,一个只是普通函数函数指针,所以主要查看宏 INITFUNC(func,level)以及INITCLASS(class,func,level)是怎么使用的,现就源码举具体例子:
(1)INITFUNC(func,level)使用
将以上部分INITFUNC(constructors, CTOR)完全展开:
分析来看就是 __initcall_constructors符号所知的函数指针constructors被放在了指定名为**".init2"输入段,所以目前在initStart与initEnd之间布局如下:
initStart ------>".init"输入段开始地址
__initcall_constructors ------>constructors
initEnd ------>".init"输入段结束地址
这样在kmain函数中:
INITRUN(&initStart, &initEnd);
就调用了__initcall_constructors (),即constructors(),完成了函数调用。
(2)INITCLASS(class,func,level)使用
同样可以试着展开宏,Memory是一个class,最后的结果是
void Memory::initialize()函数地址被放在了指定名为".init0"的输入段,那么最后
initStart与initEnd之间布局如下:
initStart ------>".init*“输入段开始地址
__initcall_Memory_initialize ------> Memory::initialize
__initcall_constructors ------>constructors
initEnd ------>”.init*"输入段结束地址
基本上相关类构造函数以及方法函数的初始化过程都是如此,而且共分为五个输入段
“.init0”、".init1"、".init2"、".init3"以及".init4":
剩余的就是追踪以上部分被引用的地方以及相关函数就可完全理解类构造函数以及方法函数的初始化过程,可以按指定输入段".init*"顺序排序查看函数调用,分析各部分源码的作用。