4、VxWorks启动流程概览:从上电到系统Shell的完整链路

各位好,我是老张。今天咱们聊聊VxWorks的启动流程。说实话,这玩意儿我刚开始接触的时候也觉得挺玄乎——按一下电源键,黑乎乎的终端上刷刷刷滚过一堆字符,然后Shell就出来了。这中间到底发生了什么?

嗯,今天我就把这层窗户纸捅破。从上电那一刻开始,到你能敲出i命令看到任务列表,咱们一步步拆解。我保证,看完这一章,你脑子里会有一条清晰的链路图。

4.1 启动流程的五个阶段

VxWorks的启动,说白了就是五个大阶段。我习惯把它们记成:上电→BootROM→BootLoader→VxWorks内核→应用初始化。你想想看,这跟咱们早上起床的流程是不是有点像?通电(闹钟响)→自检(睁眼)→加载内核(洗漱)→启动Shell(出门上班)。

咱们用一张表格快速过一下每个阶段的核心任务:

阶段 执行代码 核心任务 典型耗时
1. 上电复位 CPU硬件逻辑 PC指向复位向量,初始化CPU寄存器 微秒级
2. BootROM ROM中的固化代码 最小硬件初始化,加载BootLoader 毫秒级
3. BootLoader bootrom或uboot 加载VxWorks镜像到RAM,传递启动参数 秒级
4. 内核初始化 VxWorks内核代码 中断向量、内存管理、任务调度就绪 百毫秒级
5. 应用启动 usrRoot任务 创建Shell、加载驱动、启动应用 秒级
我的经验:排查启动故障时,先看卡在哪个阶段。如果串口连BootROM的打印都没有,八成是硬件复位或时钟问题。如果BootLoader能跑但内核起不来,重点查内存映射和镜像格式。

4.2 第一阶段:上电复位与BootROM

按下电源键,CPU的复位引脚被拉高。这时候CPU会干一件很机械的事——从复位向量地址取第一条指令。对于PowerPC架构,这个地址通常是0xFFF00100;ARM的话一般是0x00000000

这段地址里放的是什么?就是BootROM。它是固化在芯片内部或外部Flash最开头的一小段代码。我见过很多新手以为BootROM就是VxWorks的一部分,其实不是。BootROM是芯片厂商或板级厂商写的,跟VxWorks半毛钱关系都没有。

BootROM的任务很简单:

  • 初始化CPU最基本的运行环境(关中断、设栈指针)
  • 初始化内存控制器(至少让DDR能工作)
  • 从Flash或网络加载BootLoader到RAM
  • 跳转到BootLoader的入口

这里有个坑。我曾经在一个PPC项目上,BootROM死活加载不了BootLoader。查了两天,最后发现是Flash的时序配置参数写错了。BootROM里那段初始化代码是汇编写的,读起来跟天书一样。嗯,从那以后我养成了一个习惯——拿到新板子,第一件事就是确认BootROM能不能正常打印字符。

4.3 第二阶段:BootLoader的使命

BootLoader进入RAM后,系统才算真正有了点「智能」。VxWorks的BootLoader通常叫bootrom,它有两种形态:

  • bootrom(非压缩):直接可执行,体积大,调试方便
  • bootrom_uncmp:自解压版本,节省Flash空间

BootLoader的核心工作流程,我用伪代码表示一下:

bootInit() {
    1. 初始化串口(让你能看到打印信息)
    2. 初始化网卡(如果支持网络启动)
    3. 解析启动参数(bootline)
    4. 根据启动设备加载VxWorks镜像:
       - 从Flash加载(fd=0)
       - 从网络TFTP加载(fe=0:192.168.1.100)
       - 从硬盘加载(fs=0)
    5. 解压并校验镜像
    6. 跳转到VxWorks入口(sysInit)
}

启动参数(bootline)是BootLoader的灵魂。我见过最离谱的一次故障,是同事把启动参数里的IP地址写成了192.168.1.256,BootLoader解析失败直接死循环。你想想看,IP地址哪来的256?

避坑指南:我曾经在调试一个网络启动问题时,发现BootLoader能ping通服务器,但就是加载不了镜像。最后用Wireshark抓包才发现,是TFTP服务器的块大小设置太大,BootLoader的缓冲区装不下。把块大小改成512字节就解决了。

4.4 第三阶段:VxWorks内核初始化

BootLoader跳转到sysInit后,VxWorks内核开始接管。这一步非常关键,因为从此刻起,系统从「裸机状态」进入了「操作系统状态」。

内核初始化的顺序是这样的:

  1. sysInit(汇编阶段):关中断、设置栈指针、清零BSS段。说白了就是给C语言运行铺好路。
  2. usrInit(C语言阶段):初始化Cache、MMU、中断控制器。这时候中断还是关着的。
  3. usrKernelInit:创建第一个任务——usrRoot。然后开中断,系统开始调度。

这里有个细节很多人会忽略。在usrInit里,VxWorks会调用usrCacheEnable()使能Cache。如果Cache配置不对,系统跑起来会莫名其妙地死机。我遇到过一例,板子上的L2 Cache是512KB,但BSP里配成了256KB,结果一跑网络协议栈就挂。查了三天,最后用cacheShow命令一看,真相大白。

4.5 第四阶段:usrRoot任务与Shell的诞生

usrRoot是VxWorks启动后的第一个任务,优先级最高(通常是0)。它干的事情,说白了就是「搭台子」——把操作系统该有的服务都拉起来。

我整理了一下usrRoot的典型初始化序列:

步骤 函数调用 说明
1 usrIosCoreInit() 初始化I/O系统,文件描述符表
2 usrNetInit() 初始化网络协议栈(MUX、TCP/IP)
3 usrShellInit() 创建Shell任务,绑定到串口
4 usrAppInit() 用户自定义的应用程序入口

Shell的创建,其实就是一个taskSpawn调用:

taskSpawn("tShell", 1, 0, 20000, (FUNCPTR) shellTask, 
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0);

注意看,Shell任务的优先级是1(仅次于usrRoot),栈大小20KB。当你在串口上看到->提示符时,说明Shell已经成功创建并开始等待输入了。

注意:如果usrAppInit()里写了死循环或者阻塞了很长时间,Shell可能迟迟出不来。我见过有人把一个大循环写在应用初始化里,结果等了30秒才看到Shell提示符。正确的做法是:应用初始化只做启动准备,别在里面做耗时操作。

4.6 启动流程的完整时序图

为了让你有个直观的印象,我用文字描述一下完整的时序:

时间轴(从上到下)
│
├─ [0ms] 上电复位,CPU从0xFFF00100取指
├─ [0.1ms] BootROM初始化DDR,加载bootrom到0x00100000
├─ [5ms] BootROM跳转到bootrom入口
├─ [10ms] bootrom打印"VxWorks Boot 6.9"
├─ [15ms] bootrom从Flash加载VxWorks镜像到0x00200000
├─ [20ms] bootrom跳转到sysInit
├─ [21ms] sysInit完成,进入usrInit
├─ [30ms] usrInit完成,创建usrRoot任务
├─ [35ms] usrRoot初始化I/O系统
├─ [40ms] usrRoot初始化网络
├─ [45ms] usrRoot创建Shell任务
├─ [50ms] Shell打印"->",等待用户输入
└─ [50ms+] 你敲下"i",系统响应

从按下电源到看到Shell,整个过程大约50毫秒。当然,这是理想情况。如果网络启动或者加载大镜像,时间会延长到几秒甚至几十秒。

4.7 常见启动故障速查

最后,我把自己这些年遇到的启动故障整理成一个速查表。你排查问题时可以直接对照:

现象 可能原因 排查手段
串口无任何输出 硬件复位、时钟、BootROM损坏 示波器测复位信号、检查Flash焊接
BootROM有输出,BootLoader加载失败 Flash坏块、镜像地址错误 md命令查看Flash内容
BootLoader加载成功,内核启动卡死 MMU配置、Cache配置、内存映射错误 usrInit加打印,定位卡死点
内核启动完成,Shell不出现 usrAppInit阻塞、Shell任务未创建 检查usrShellInit是否被注释
Shell出现,但命令无响应 中断控制器配置错误、串口驱动问题 intShow查看中断是否正常

好了,这一章的内容就到这儿。说白了,VxWorks的启动流程就是一条从硬件到软件的接力赛。每一棒交接的时候,都是最容易出问题的地方。你只要记住五个阶段、每个阶段的核心任务,排查故障时按图索骥,基本不会跑偏。

下一章咱们深入BootLoader,聊聊bootline的配置玄机。到时候我会分享一个我当年调了整整一周的网络启动案例,保证让你少走弯路。