第3章 VxWorks操作系统启动过程详解
本章将着重分析VxWorks的启动过程,首先,简单介绍VxWorks操作系统支持的几种启动方式,每种启动方式下映像的组成和基本启动流程;其次,详细介绍下载启动方式下bootrom的组成和执行流程;再次,详细介绍VxWorks本身的启动过程;最后,详细介绍BSP下的文件组成。
3.1 VxWorks操作系统启动
3.1.1 VxWorks基本启动方式
按VxWorks内核的下载形式,VxWorks启动总体上分为两种方式:下载型和ROM型。
● 下载型启动方式:bootrom+VxWorks。此时bootrom被烧入ROM中,而VxWorks内核映像通过串口或者网口下载到系统RAM中。
● ROM型启动方式:VxWorks直接从ROM中运行,不借助于bootrom引导程序。此时VxWorks内核映像被烧入ROM中,无bootrom程序。
对于不同的启动方式,VxWorks操作系统内核映像的组成也略有不同。
bootrom引导程序一般称为VxWorks boot Image。该程序由如下文件创建而成:romInit.s、bootInit.c、sysALib.s、sysLib.c、bootConfig.c、设备驱动程序。
注意
虽然bootrom中包含sysALib.s文件,但是并未使用其中定义的任何函数,这与ROM形式的VxWorks内核映像类似。
VxWorks内核映像一般被称为VxWorks Application Image。VxWorks内核映像是系统启动后实际运行的程序(操作系统本身)。对于下载形式的VxWorks内核映像,由于需要对硬件环境进行重新初始化(即不依赖于romInit函数所做的初始化工作),所以最先需要运行sysInit函数进行重新初始化,这些初始化工作与romInit函数基本相同,但是不再需要对内存控制器进行重新初始化,这是sysInit区别于romInit函数之处。sysInit最后跳转到usrInit函数。而对于ROM方式直接运行的VxWorks内核映像,由于romInit.s函数此时是作为内核映像的一部分,所以就省去了对sysInit函数的调用。下载型和ROM型在VxWorks内核文件的构成上有一些差别。
● 下载形式的VxWorks内核映像组成:sysALib.s、sysLib.c、usrConfig.c、设备驱动程序文件。
● ROM形式的VxWorks内核映像组成:romInit.s、bootInit.c、sysALib.s、sysLib.c、usrConfig.c、设备驱动程序文件。
注意
虽然ROM形式的VxWorks内核映像并未使用sysALib.s文件中的函数(sysInit),但是该文件仍然作为一部分存在于该VxWorks内核映像中。当然用户可以修改defs.bsp文件中的MACH_DEP宏定义,将sysALib.o从如下形式定义中去掉即可。
MACH_DEP =|sysLib.o| sysLib.o $(MACH_EXTRA) $(ADDED_MODULES)
3.1.2 VxWorks操作系统内存布局
在VxWorks操作系统过程中可能使用到的BootRom和VxWorks内核映像本身都可以存在两种方式:压缩的和非压缩的。
1.非压缩形式
如果没有进行压缩,则只有一次重定位,即从ROM到RAM只存在一次代码复制过程,所有ROM中存储的代码都被一次性复制到RAM中。
① 对于BootRom而言,所有的代码被一次性复制到RAM_HIGH_ADRS指定地址处,如图3-1所示。
图3-1 非压缩版BootRom引导程序ROM及载入RAM后分布图
② 对于VxWorks_rom而言,所有的代码被一次性复制到RAM_LOW_ADRS指定地址处,如图3-2所示。
图3-2 非压缩版VxWorks_rom内核映像类型ROM及载入RAM后布局图
2.压缩形式
如果有进行压缩,则代码复制过程将分为两次,一次是非压缩代码,另一次是压缩代码,且二者复制到内存不同的位置处。
① 对于BootRom而言,非压缩代码(romInit.s、bootInit.c)被直接复制到RAM_LOW_ADRS处;压缩代码复制到RAM_HIGH_ADRS处,并在复制过程中完成解压缩,如图3-3所示。
图3-3 压缩版BootRom引导程序ROM及载入RAM后分布图
② 对于VxWorks_rom而言,非压缩代码(romInit.s、bootInit.c)被直接复制到RAM_HIGH_ADRS处;压缩代码复制到RAM_LOW_ADRS处,并在复制过程中完成解压缩,如图3-4所示。
图3-4 压缩版VxWorks_rom内核映像类型ROM及载入RAM后分布图
事实上,对于压缩版本,在进行编译时,进行了两次代码链接:一次是针对压缩代码的链接,另一次是将非压缩代码和压缩代码整合在一起时的链接。注意,在进行非压缩代码和压缩代码的整合时,压缩代码部分是作为数据进入整个映像的,所以不会对其进行重新链接。对压缩代码的链接,实际上是在代码被压缩之前完成的,完成代码的链接后,生成特定格式的目标文件(如ELF或者COFF),为了在解压缩后可以直接执行,首先必须将其转成二进制可执行文件,通过objcopy函数完成此项功能,此后对生成的二进制可执行文件调用deflate函数进行压缩,由于需要将压缩后的代码最后整合到整个映像中,故压缩后的文件还需要通过binToAsm工具进行转换,转成一个汇编源文件,该文件将压缩后的二进制代码作为数据部分进行保存,从而避免在与非压缩代码最后进行二次链接时被修改。
两步链接过程中各自指定了不同的链接地址,对于压缩代码而言,其分为两种情况,对于BootRom,压缩代码被链接到了RAM_HIGH_ADRS地址处,而对于VxWorks_rom,则被链接到了RAM_LOW_ADRS地址处,在与非压缩代码进行整合链接时指定的链接地址(即_romInit函数地址)与压缩代码链接时指定的地址相对应:如果压缩代码为RAM_HIGH_ADRS,则最后整合时就为RAM_LOW_ADRS;如果压缩代码为RAM_LOW _ADRS,则整合时就为RAM_HIGH_ADRS。
3.1.3 下载型启动方式概述
下载形式的VxWorks启动方式需要bootrom引导程序,该程序将被烧录到开发板的ROM或者Flash中,在上电时,系统将自动跳转到ROM或者Flash起始地址处运行该bootrom引导程序,该引导程序进行必要的系统初始化,从而为下载VxWorks内核映像做准备,如通过网口下载时,则需要先调用网口驱动程序进行网口初始化。
由于bootrom的主要工作是下载真正的VxWorks内核映像到系统RAM中,所以其进行的初始化工作也是为这一目的,在文件组织上,虽然从上文中可以看到其与VxWorks内核映像的文件组成非常相似,但其使用bootConfig.c文件,而VxWorks内核映像则使用usrConfig.c文件。这两个文件中虽然定义有相同的函数名(如usrInit、usrRoot),但在实现上存在很大的差异:bootConfig.c文件实现完成从某个服务器下载真正的VxWorks内核映像到系统RAM中(由变量RAM_LOW_ADRS指向的内存地址处),并跳转到sysInit函数(定义在sysALib.s文件中第一个且必须是第一个函数,该函数即被装载到变量RAM_LOW_ADRS指向的内存地址处)执行;usrConfig.c文件实现完成一个操作系统正常运行所需要的所有初始化工作。简言之,bootConfig.c文件完成VxWorks内核映像的下载,而usrConfig.c完成初始化VxWorks操作系统的所有工作。
按bootrom执行方式的不同,bootrom文件存在如下三种文件类型。
(1)bootrom.bin
它为压缩bootrom文件形式,此种文件形式主要为解决开发板上ROM或者Flash的空间限制。注意:压缩并非对所有的文件进行压缩,而是除了romInit.s和bootInit.c文件的其他所有文件,因为解压缩程序必须是非压缩形式的。
bootrom执行流程如下(以下采用“文件名:函数名:初始是否被压缩”形式进行说明)。
① romInit.s:romInit:非压缩。
romInit函数完成系统硬件环境下必要的初始化工作,如从系统角度禁止中断,初始化相关寄存器到可知状态,初始化内存控制器,初始化函数调用所需的栈,最后跳转到romStart函数执行。
② bootInit.c:romStart:非压缩。
romStart函数主要完成如下任务:
首先将romInit函数及其自身复制到RAM_LOW_ADRS变量指向的RAM区,以便从RAM执行。注意,此后在下载VxWorks内核映像时,内核映像也被下载到RAM_LOW_ADRS变量指向的内存处,所以这部分代码此后将被覆盖,同样,bootrom占用的所有其他内存最后也将被VxWorks内核回收(如由RAM_HIGH_ADRS指向的内核空间)。
● 将bootrom程序其他部分(压缩部分)从ROM区复制到RAM区(由RAM_HIGH_ADRS变量指向的内存地址处),并解压缩。
● 对于cold boot(冷启动)方式,将其复制到RAM区清零。
● 最后跳转到usrInit(注意:是bootConfig.c文件中定义的)函数进行执行。
③ bootConfig.c:usrInit:压缩。
usrInit函数此处主要进行外围硬件初始化,为下载VxWorks内核映像做初始准备,而后创建tUsrBoot进程调用usrRoot程序进一步完成驱动程序初始化工作,为下载VxWorks内核映像做进一步工作,最后创建tBoot进程调用bootCmdLoop函数完成VxWorks的内核映像下载。在下载完成后,调转到下载起始地址处执行,此时下载型VxWorks内核入口函数sysInit将被调用执行,真正开始VxWorks操作系统的启动过程。最终在usrConfig.c:usrRoot函数执行完毕后,VxWorks操作系统即完成启动,系统进入正常运行状态。函数执行流程如下:(采用“映像类型:文件名:函数名”形式)
bootrom:romInit.s:romInit→bootrom:bootInit.c:romStart→bootrom:bootConfig.c:usrInit→bootrom:bootConfig.c:usrRoot→bootrom:bootConfig.c:bootCmdLoop→完成VxWorks内核映像下载,并跳转到VxWorks内核入口函数(sysInit)执行代码→VxWorks:sysALib.s:sysInit→VxWorks:usrConfig.c:usrInit→VxWorks:usrConfig.c:usrRoot→VxWorks操作系统完成启动。
如图3-5所示为bootrom完成下载VxWorks内核映像后的RAM布局,此时,之前bootrom占用的所有区域均被VxWorks操作系统回收。
图3-5 VxWorks操作系统启动后内存分布图
(2)bootrom_uncmp.bin和bootrom_res.bin
上述两种形式的bootrom执行流程基本与bootrom.bin相同。差别在于:
① bootrom_uncmp.bin。
该文件类型为非压缩类型,所以区别以上压缩形式的地方在于romStart函数将bootrom从ROM移动到RAM中时,无须进行解压缩工作,移动到的RAM区地址仍然由RAM_HIGH_ADRS变量指定。注意,对于非压缩bootrom,所有的代码(包括已经执行的romInit函数、当前执行的romStart函数、构成bootrom的其他所有函数)都一次性从ROM复制到RAM中,RAM中目的地址由RAM_HIGH_ADRS变量指定,如图3-1所示。函数执行流程同压缩版本。
② bootrom_res.bin。
res全称为resident,此处的意思为程序代码部分将驻留在ROM中,所以不同于上文中的地方,即romStart函数只复制bootrom引导程序的数据段到RAM_HIGH_ADRS变量指向的RAM区。代码执行还是从ROM区中取,此种方式一般在开发板ROM区较大时采用,但会增加执行时间,另外,由于需要从ROM中直接执行,故也不能采用代码压缩方式。
下载方式中使用的VxWorks内核映像一般命名为“VxWorks”(不包含内核符号表,此时创建一个独立的内核符号表文件VxWorks.sym,如果需要将内核符号表包含其中,则在config.h文件中定义INCLUDE_STANDALONE_SYM_TBL组件)或者“VxWorks_st”(内含内核符号表),而其他启动方式(一般就为ROM启动方式)下的VxWorks内核映像名都具有一个后缀,如VxWorks_rom表示从ROM直接启动,VxWorks_res_rom表示代码部分驻留在ROM中,当然也是从ROM直接启动。下载方式下,VxWorks内核映像文件构成以及完成下载后VxWorks内核函数的调用流程在前文已有介绍,此处不再赘述。
3.1.4 ROM型启动方式概述
所有的VxWorks内核映像类型中,只有VxWorks类型使用上文中介绍的bootrom引导程序进行启动,此时VxWorks内核映像放置在主机端,由目标板bootrom完成VxWorks的下载,一般通过网口(或串口)方式进行。其他类型(文件名中带有rom字样的)无须bootrom的配合,也即不需要bootrom。因为这些VxWorks类型的内核映像自身(而非bootrom)被烧入开发板系统ROM或者Flash中,均无须进行下载,系统上电时,将直接跳转到VxWorks内核映像入口函数执行操作系统的初始化工作。
ROM型启动方式下的VxWorks内核映像类型有以下几类。
① VxWorks_rom.bin:非压缩版VxWorks内核映像类型不含内核符号表。如果需要包含内核符号表,则在config.h文件中定义INCLUDE_STANDALONE_SYM_TBL组件。
② VxWorks.res_rom.bin:非压缩版代码驻留ROM中执行的VxWorks内核映像类型(含内核符号表)。
③ VxWorks.res_rom_nosym.bin:非压缩版代码驻留ROM中执行的,不包含内核符号表的VxWorks内核映像类型。
④ VxWorks.st_rom.bin:压缩版本的内含内核符号表的VxWorks内核映像类型。
⑤ VxWorks_romCompress.bin:压缩版的不含内核符号表的VxWorks内核映像类型。
不同的VxWorks内核版本下,在内核映像名称上会有所变化,但一般都包含在以上类型中。从这些映像类型来看,主要有以下区分:
● 是否为ROM驻留方式,即代码是否被复制到RAM中执行。
● 是否进行了压缩。
● 是否包含符号表。
注意
VxWorks内核映像是否包含内核符号表对于后续函数的调用非常重要,如在串口命令行下输入一个函数名称,如果内核映像不包含内核符号表,即使在内核中包含该函数定义,在终端也会给出“undefined symbol”错误。
以下从三个不同角度介绍它们的不同点,首先以VxWorks_rom.bin文件类型介绍执行的基本流程(采用“文件名:函数名”方式)。
1)romInit.s:romInit。
此处romInit函数完成的工作同bootrom,实际上,二者使用相同的romInit函数实现。
2)bootInit.c:romStart。
首先将其自身复制到RAM_HIGH_ADRS变量指向的RAM区,以便从RAM执行。注意与上文中bootrom的区别,虽然使用的是相同的romStart函数实现,但在RAM地址上存在差别,关键点在于VxWorks内核映像被复制到RAM_LOW_ADRS指向的内存地址处。
romStart函数完成如下任务:
● 由于是非压缩版本的VxWorks内核映像,故所有的代码一次性从ROM复制到由RAM_LOW_ADRS变量指向的RAM内存处。
● 对于cold boot(冷启动)方式,将其他BSS区清零。
● 最后跳转到usrInit(注意:是usrConfig.c文件中定义的)函数进行执行。
3)此后的执行流程为:usrConfig.c:usrInit→usrConfig.c:usrRoot。usrConfig.c:usrRoot函数执行完毕后,VxWorks操作系统即已完成启动,系统进入正常运行状态。
其他ROM型VxWorks内核映像启动流程差别如下:
1)是否为ROM驻留方式。
对于ROM驻留方式而言,在以上第一步中只将数据部分从ROM载入到由RAM_LOW_ADRS指向的内存区域,代码仍然滞留在ROM中,此后一直从ROM中读取代码执行,此种执行方式造成效率的部分损失,一般也较少使用。涉及的文件类型为:
VxWorks.res_rom.bin和VxWorks.res_rom_nosym.bin。
2)是否进行了压缩。
对于压缩的VxWorks内核映像,在以上第一步复制中,将分两个阶段完成:第一阶段完成非压缩代码(romInit函数、romStart函数)的复制,这部分代码被复制到RAM_HIGH_ADRS指向的RAM内存处;第二阶段完成压缩代码的解压缩和复制过程,这部分代码被解压缩到RAM_LOW_ADRS指向的RAM内存处。
3)是否包含了符号表。
所谓符号表,是内核中定义的所有函数与其(虚拟)地址的对应关系表。符号表与VxWorks内核映像一般是分离的,为了调试方便,一般需要独立的载入符号表,当然也可以将符号表纳入VxWorks内核映像中作为一个整体。VxWorks.st_rom.bin文件类型即将符号表作为了内核映像的一部分。
符号表最大的作用是在命令行直接输入函数名即可运行该函数,内核查询符号表获得对应的地址并转到该地址处执行。另外,在调试时,也可以对地址进行函数名标注,从而方便调试,符号表与VxWorks内核通常是独立的,所以无论是否包含符号表,都不会对内核初始化流程造成影响。当VxWorks内核映像被载入RAM后,进入usrInit函数执行时,最终分布图如图3-5所示。
ROM型VxWorks内核映像类型启动流程总结如下(由于其中只涉及VxWorks内核,故采用“文件名:函数名”方式)。
romInit.s:romInit→bootInit.c:romStart→完成VxWorks内核从ROM到RAM的复制(和解压缩)→usrConfig.c:usrInit→usrConfig.c:usrRoot→VxWorks操作系统启动完成。