FreeBSD的Loader和内核初始化

loader也是一个 BTX 客户,在这里不作详述 。已有一部内容全面的手册 loader(8) ,由Mike Smith书写 。比loader更底层的BTX的机理已经在前面讨论过 。loader 的主要任务是引导内核 。当内核被装入内存后,即被loader调用:sys/boot/common/boot.c:
/* 从loader中调用内核中对应的exec程序 */
module_formats[km->m_loader]->l_exec(km);loader跳转至哪里呢?那就是内核的入口点 。让我们来看一下链接内核的命令:sys/conf/Makefile.i386:ld -elf -Bdynamic -T /usr/src/sys/conf/ldscript.i386 -export-dynamic
-dynamic-linker /red/herring -o kernel -X locore.o在这一行中有一些有趣的东西 。首先,内核是一个ELF动态链接二进制文件,可是动态链接器却是/red/herring,一个莫须有的文件 。其次,看一下文件sys/conf/ldscript.i386,可以对理解编译内核时ld的选项有一些启发 。阅读最前几行,字符串sys/conf/ldscript.i386:ENTRY(btext)
表示内核的入口点是符号 `btext' 。这个符号在locore.s中定义:sys/i386/i386/locore.s:
.text
/**********************************************************************
*
* This is where the bootblocks start us, set the ball rolling...
* 入口
*/
NON_GPROF_ENTRY(btext)
首先将寄存器EFLAGS设为一个预定义的值0x00000002,然后初始化所有段寄存器:sys/i386/i386/locore.s
/* 不要相信BIOS给出的EFLAGS值 */
pushl$PSL_KERNEL
popfl
/*
* 不要相信BIOS给出的%fs、%gs值 。相信引导过程中设定的%cs、%ds、%es、%ss值
*/
mov %ds, %ax
mov %ax, %fs
mov %ax, %gs
btext调用例程recover_bootinfo(),identify_CPU(),create_pagetables() 。这些例程也定在locore.s之中 。这些例程的功能如下:recover_bootinfo
这个例程分析由引导程序传送给内核的参数 。引导内核有3种方式:由loader引导(如前所述), 由老式磁盘引导块引导,无盘引导方式 。这个函数决定引导方式,并将结构struct bootinfo存储至内核内存 。identify_CPU 这个函数侦测CPU类型,将结果存放在变量_cpu中 。create_pagetables 这个函数为分页表在内核内存空间顶部分配一块空间,并填写一定内容 下一步是开启VME(如果CPU有这个功能):
testl$CPUID_VME, R(_cpu_feature)
jz 1f
movl%cr4, 陎
orl $CR4_VME, 陎
movl陎, %cr4
然后,启动分页模式:/* Now enable paging */
movlR(_IdlePTD), 陎
movl陎,%cr3/* load ptd addr into mmu */
movl%cr0,陎/* get control Word */
orl $CR0_PE|CR0_PG,陎 /* enable paging */
movl陎,%cr0/* and let's page NOW! */
由于分页模式已经启动,原先的实地址寻址方式随即失效 。
随后三行代码用来跳转至虚拟地址:pushl$begin
/* jump to high virtualized address */
ret
/* 现在跳转至KERNBASE,那里是操作系统内核被链接后真正的入口 */
begin:
函数init386()被调用;随参数传递的是一个指针,指向第一个空闲物理页 。
随后执行mi_startup() 。init386是一个与硬件系统相关的初始化函数,mi_startup()是个与硬件系统无关的函数(前缀'mi_'表示Machine Independent,不依赖于机器) 。内核不再从mi_startup()里返回;调用这个函数后,内核完成引导:sys/i386/i386/locore.s:
movlphysfree, %esi
pushl%esi/* 送给init386()的第一个参数 */
call_init386/* 设置386芯片使之适应Unix工作 */
call_mi_startup /* 自动配置硬件,挂接根文件系统,等 */
hlt /* 不再返回到这里! */
1.7.1 init386()init386()定义在sys/i386/i386/Machdep.c中,它针对Intel 386芯片进行低级初始化 。loader已将CPU切换至保护模式 。loader已经建立了最早的任务 。译者注: 每个"任务"都是与其它“任务相对独立的执行环境 。
任务之间可以分时切换,这为并发进程/线程的实现提供了必要基础 。对于Intel 80x86任务的描述,在这个任务中,内核将继续工作 。在讨论其代码前,我将处理器对保护模式必须完成的一系列准备工作一并列出:

推荐阅读