继续分析原来的代码,现在已经进入一个比较重要的函数
dbginit()
,因此这个函数相当复杂的功能调用。
/*
* Init PMON and debug
*/
cpuinfotab[0] = &DBGREG;
dbginit(NULL);
第一行代码
cpuinfotab[0]
保存
DBG
寄存器值,主要包括
32
个通用寄存器和
CP0
的
32
寄存器,以及两个乘法除法这寄存器。
现在就来仔细地查看
dbginit
(NULL)
的实现,代码如下:
/*
* PMON2000 entrypoint. Called after initial setup.
*/
void
dbginit
(char *adr)
{
int memsize, freq;
char fs[10], *fp;
char *s;
/* splhigh();*/
memsize = memorysize;
上面保存低端
256M
内存大小。
__init(); /* Do all constructor initialisation */
上面调用初始化
C++
的构造函数初始化和全局的静态变量。
SBD_DISPLAY ("ENVI", CHKPNT_ENVI);
envinit ();
上面初始化环境变量。
#if
defined(SMP)
/* Turn on caches unless opted out */
if (!getenv("nocache"))
md_cacheon();
#endif
上面打开缓存。
SBD_DISPLAY ("SBDD", CHKPNT_SBDD);
tgt_devinit();
上面特定的主板初始化。
#ifdef
INET
SBD_DISPLAY ("NETI", CHKPNT_NETI);
init_net (1);
#endif
上面选择网络初始化。
#if
NCMD_HIST > 0
SBD_DISPLAY ("HSTI", CHKPNT_HSTI);
histinit ();
#endif
上面初始化是否缓存过去的命令。
#if
NMOD_SYMBOLS > 0
SBD_DISPLAY ("SYMI", CHKPNT_SYMI);
syminit ();
#endif
上面选择是否设置符号表初始化。
#ifdef
DEMO
SBD_DISPLAY ("DEMO", CHKPNT_DEMO);
demoinit ();
#endif
上面选择是否初始化演示功能。
SBD_DISPLAY ("SBDE", CHKPNT_SBDE);
initial_sr |= tgt_enable (tgt_getmachtype ());
上面保存状态寄存器。
#ifdef
SR_FR
Status = initial_sr & ~SR_FR; /* don't confuse naive clients */
#endif
/* Set up initial console terminal state */
ioctl(STDIN, TCGETA, &consterm);
上面初始化终端输出。
#ifdef
HAVE_LOGO
tgt_logo();
#else
printf ("/n * PMON2000 Professional *");
#endif
printf ("/nConfiguration [%s,%s", TARGETNAME,
BYTE_ORDER == BIG_ENDIAN ? "EB" : "EL");
上面显示
LOGO
和显示字节顺序。龙芯是小端格式。
#ifdef
INET
printf (",NET");
#endif
#if
NSD > 0
printf (",SCSI");
#endif
#if
NWD > 0
printf (",IDE");
#endif
printf ("]/nVersion: %s./n", vers);
printf ("Supported loaders [%s]/n", getExecString());
printf ("Supported filesystems [%s]/n", getFSString());
printf ("This software may be redistributed under the BSD copyright./n");
上面显示提示信息。
tgt_machprint();
上面显示厂家信息。
freq = tgt_pipefreq ();
sprintf(fs, "%d", freq);
fp = fs + strlen(fs) - 6;
fp[3] = '/0';
fp[2] = fp[1];
fp[1] = fp[0];
fp[0] = '.';
printf (" %s MHz", fs);
上面显示总线的频率。
freq = tgt_cpufreq ();
sprintf(fs, "%d", freq);
fp = fs + strlen(fs) - 6;
fp[3] = '/0';
fp[2] = fp[1];
fp[1] = fp[0];
fp[0] = '.';
printf (" / Bus @ %s MHz/n", fs);
上面显示
CPU
频率。
printf ("Memory size %3d MB (%3d MB Low memory, %3d MB High memory) ./n", (memsize+memorysize_high)>>20,
(memsize>>20), (memorysize_high>>20));
tgt_memprint();
上面显示内存的大小。
#if
defined(SMP)
tgt_smpstartup();
#endif
上面选择多
CPU
的初始化。
printf ("/n");
md_clreg(NULL);
md_setpc(NULL, (int32_t) CLIENTPC);
md_setsp(NULL, tgt_clienttos ());
上面清空保存寄存器的变量,并保存新的
PC
值和堆栈值。
#ifdef
AUTOLOAD
s = getenv ("al");
autoload (s);
#else
s = getenv ("autoboot");
autorun (s);
#endif
上面设置是否自动加载操作系统文件。
}
这个函数做了很多工作,下面要仔细地分析每个子函数实现的功能,才能理解后面所做的工作,否则也不知道后面会做些什么。又进入下一层函数分析它的实现。
首先来了解怎么样初始化
C
++的构函数和全局静态变量。仔细地看一下函数
__init()
,如下:
void
__init
()
{
static int initialized = 0;
/*
* Call global constructors.
* Arrange to call global destructors at exit.
*/
if (!initialized) {
initialized = 1;
__ctors();
}
}
在这个函数里,先判断是否已经初始化全局函数和构造函数,如果没有初始化,就设置为已经初始化,接着调用函数初始化全局
__ctors
()
函数。也许你会问,这些全局函数从那里来的呢?
如果你去看看
GCC
的连接说明文件,就会发现有这个段在那里,因此这些全局函数是由编译器生成的,并不是由用户定义。如果不调用这些全局初始化函数,很多全局变量是没有定义的值,本来你编程时初始化为
100
的值,可能只是
0
或者任意的数值。现在又立即去看函数
__ctors
()
的实现,如下:
static
void
__ctors
()
{
void (**p)(void) = __CTOR_LIST__ + 1;
while (*p)
(**p++)();
}
由于
GCC
编译器会根据连接文件生成全局调用函数,下面就是连接文件相关内容,如下:
.ctors :
{
__CTOR_LIST__ = .;
LONG((__CTOR_END__ - __CTOR_LIST__) / 4 - 2)
*(.ctors)
LONG(0)
__CTOR_END__ = .;
}
.dtors :
{
__DTOR_LIST__ = .;
LONG((__DTOR_END__ - __DTOR_LIST__) / 4 - 2)
*(.dtors)
LONG(0)
__DTOR_END__ = .;
}
编译器就会根据上面的连接脚本生成全局初始化函数和全局析构函数,并且每个函数入口是按照4字节的指针排列的。因此,在函数
__ctors
()
里循环地调用所有函数运行一遍。后面析构函数相应也调用函数
__dtors
()
来实现的。