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、加载驱动、启动应用 | 秒级 |
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?
4.4 第三阶段:VxWorks内核初始化
BootLoader跳转到sysInit后,VxWorks内核开始接管。这一步非常关键,因为从此刻起,系统从「裸机状态」进入了「操作系统状态」。
内核初始化的顺序是这样的:
- sysInit(汇编阶段):关中断、设置栈指针、清零BSS段。说白了就是给C语言运行铺好路。
- usrInit(C语言阶段):初始化Cache、MMU、中断控制器。这时候中断还是关着的。
- 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的配置玄机。到时候我会分享一个我当年调了整整一周的网络启动案例,保证让你少走弯路。