Professional Documents
Culture Documents
VxWorks基础及开发实例 VxSim软件仿真实例
VxWorks基础及开发实例 VxSim软件仿真实例
集成仿真工具(VxSim)是一个软件原型仿真器,主要是使开发者在无实际目标硬件情
况下,先进行原型级应用程序的开发,包括网络设计和基于多处理器的设计。VxSim 还可使
开发者在开发周期中较早地进行大部分的应用软件测试,从而能够以较小的代价纠正错误。
本实验的目的是使读者在不使用实际目标硬件的情况下,了解 VxWorks 软件仿真工具 VxSim
的使用方法,并了解 Tornado 中各种辅助调试工具的使用方法。
1.1 编写、编译程序
图 1-2 创建新工程
可启动的工程:是一个链接到 VxWorks 映象的应用程序。该工程中不仅包括应用程序的
代码,还包括 VxWorks 内核代码。在工程管理器中可以非常地添加或删除一些内核组件, (如
操作系统中的 ANSI C 组件和 POSIX 组件等),从而调整 VxWorks 内核的特征。通过组件的
添加或删除,一方面可根据需要调整 VxWorks 内核的代码大小(嵌入式系统的资源都是非常
有限的,保持内核的高度精简很必要);另一方面还可保证整个系统运行所需的组件都包含
在所生成的目标代码中。当目标机启动后,该应用程序会被自动加载且运行。
图 1-3 输入新建工程的详细信息
可下载的工程:是指该工程所编译的目标代码是一种可重定位的代码。在一个可下载的
工程中,可以包含多个可重定位的模块。这些模块可被动态下载到一个运行有 VxWorks 内核
的目标系统中,并可通过 WindShell 或者调试器来启动。动态加载是 Tornado 的一个特点。
通过这项功能,可将目标模块装载到一个正在运行的系统中,与重新构建和链接整个操作系
统相比,所需的时间更短,从而大大缩短了调试周期。所以在开发一个项目时,往往是先将
内核移植到目标系统中;然后采用这种动态加载的手段来调试具体的应用程序;最后在应用
程序调试无误后,将这些模块与 VxWorks 内核代码集成,最终形成一个可启动的代码。
在图 1-2 中选择 Create a bootable VxWorks image 创建一个新的可启动工程,并在图
1-3 中输入工程的名称和位置等信息。
2. 选择 BSP(基于 VxSim 模拟器)
接下来工程向导会提示输入参考 BSP,按照图 1-4 所示选择 simpc_gnu.wpj。
对于可启动的工程,在创建时首先须正确指定所参考的 BSP 以及编译器,对于 ARM 这类
处理器来说,可选择的工具链有两个,分别是 gnu 和 diab(在安装 Tornado 时须进行选择,
有的系统为了节省空间,可能省掉某个编译器,在这里就会只有某些相应的选项)。每个工
具链又可分为两个版本,分别是 gnu、gnube、diab、diabbe,这与 ARM 处理器的存储模式
有关,即大端存储(Big endian)和小端存储(Little Endian)。
如果使用后面有 be 后缀的编译器,编译出的代码应该运行在大端存储模式下;而对于
不带后缀的编译器,编译出的代码应该运行在小端存储模式下。大多数处理器在默认的情况
下都采用小端存储模式,系统会自动选择 gnu 编译器,当然在创建工程时也可选择 diab 编
译器。它们编译的目标代码并没有大的区别,只是在代码的语法上,特别是汇编代码的语法
上,有一些小小的区别,但绝大部分代码都可以在不作任何修改的情况下使用这两种编译器
编译。
由于本实验采用 VxSim 模拟器,因此直接选择一个存在的工程(An existing project)
simpc_gnu.wpj,它将自动选择 VxSim 模拟器,并采用 gnu 编译器。对于具体的目标硬件一
般选择 A BSP 然后选择具体的 BSP 包,详细设置请参考 1.3 节的实验。
图 1-4 选择创建基于 VxSim 模板的工程
3. 完成工程的创建
工程创建完毕后,会给出工程的信息,如工作取的位置和名称、工程的位置和名称以及
参考工程,如图 1-5 所示。
4. 编译上面创建的工程
通过在工程管理器中工程名的节点处右击并选择 Build ‘vxWorks.exe’编译上面新建
的工程,如图 1-6 所示。
通过选择 File→New Project…弹出图 1-7 所示的对话框,选择 Create downloadable
application modules 创建一个可下载的工程,并在图 1-8 中输入工程的名称和位置等信息。
最后如图 1-9 所示,选择编译工具 SIMNTgnu。
图 1-5 工程创建报告
图 1-6 编译工程
5. 再创建一个可下载的工程
图 1-7 创建可下载模块
图 1-8 输入可下载模块的详细信息
图 1-9 选择新建工程的编译器
6. 在 test 可下载工程中添加如下测试代码
通过选择 File→New 创建测试程序(见图 1-10)。当选中复选框 Add to project 时,
该新文件会自动加入到相应的工程中。输入程序清单 1-1 所列的代码备用。
图 1-10 为工程添加测试代码
程序清单 1-1 简单的测试代码
#include <stdio.h>
void add_test(int x, int y) {
printf("%d + %d = %d\n", x, y, x + y );
}
7. 编译 test 模块
与可启动模块一样,可通过在工程管理窗口中对应的工程名上右击并选择 Build
‘test.out’来编译该工程,如图 1-11 所示。
图 1-11 编译可下载模块
1.2 下载目标文件
图1-14 目标仿真器输出窗口
图1-15 启动目标服务器对话框
如果目标仿真器启动成功,程序将自动下载到目标仿真器,接下来可以继续进行下面的
各种调试工具的实验。
1.3 调试命令行解释器
调试命令行解释器(WindSh)是一个运行在主机上的 C 语言解释器,通过它可运行下载
到目标机上的所有函数,包括 VxWorks 内核自身的函数和应用程序所包含的函数;并且能够
解释 C 语言书写的大多数表达式,允许对变量的符号访问;WindSh 外壳还能解释常规的工
具命令语言(TCL)。另外,WindSh 还可实现以下所有调试功能:下载/删除软件模块;产
生/删除任务;设置/删除断点;运行、单步或继续执行程序;查看/修改内存、寄存器和变
量;查看任务列表、内存的使用情况以及 CPU 的利用率;查看特定的对象(任务、信号量、
消息队列、内存分区、类);复位目标机。
启动 WindSh 有多种途径,例如:在 Target server 运行起来后,直接单击工具栏中的
图 1-16 WindSh 窗口
3. 数据变换
WindSh 使用十六进制和十进制显示所有的整数和字符,也可显示字符常量、符号地址
及其偏移量。例如:
-> 31
value = 31 = 0x1f = __major_os_version__ + 0x1b
-> '1'
value = 49 = 0x31 = '1' = __major_os_version__ + 0x2d
-> 0x1234
value = 4660 = 0x1234 = __section_alignment__ + 0x234
4. 数据计算
所有的 C 运算符都可用于数据计算,并且可以使用括号“()”来设定运算的优先级,例
如:
-> (1+2)*3
value = 9 = 0x9 = __major_os_version__ + 0x5
-> (1<<2)+100
value = 104 = 0x68 = 'h' = __major_os_version__ + 0x64
-> 10.1*100
value = 1010
5. 符号表达式计算
在下面的例子中首先添加一个变量 a,随后即可在 WindSh 中引用该变量进行一些计算:
-> a=10
new symbol "a" added to symbol table.
a = 0xca0e34: value = 10 = 0xa = __major_os_version__ + 0x6
-> a1=100
new symbol "a1" added to symbol table.
a1 = 0xca0e2c: value = 100 = 0x64 = 'd' = __major_os_version__ + 0x60
-> a *2
value = 20 = 0x14 = __major_os_version__ + 0x10
-> x=a+a1
new symbol "x" added to symbol table.
x = 0xca0e24: value = 200 = 0xc8 = __major_os_version__ + 0xc4
6. 调用内置的 WindSh 函数
在 WindSh 模块中,内置了很多函数,可以把它们分为几类:任务管理、任务信息查询、
系统信息查询、调试以及目标显示等,如表 1-1 所列。
常用命令 功 能 常用命令 功 能
i 逐个显示所有任务的统计信息及状态 td 删除指定的任务
l 将指定的地址进行反汇编 tr 回复一个被挂起的任务
ld 动态加载一个可重定位模块 ts 挂起指定的任务
m 修改指定的存储器 w 逐个显示所有任务的挂起信息
7. 调用内核中和下载的函数
在 WindSh 模块中,除了可以调用内置的历程外,内核中存在的函数以及下载程序中的
函数均可以在 WindSh 中执行,而且可以添加参数。例如,如果下载程序清单 1-1 的测试程
序后,则可在 WindSh 中如下输入调用 add_test 函数。
-> add_test(10,10)
10 + 10 = 20
value = 13 = 0xd = __major_os_version__ + 0x9
1.4 调试器
源码级调试器(CrossWind Debugger)提供图形和命令行两种调试方式,可进行任务或
系统级的断点设置、单步执行及调试异常处理等。它的调试引擎是 GDB 的增强版,来自免费
软件基金会(FSF)的可移植符号调试器。GDB 命令的全部文档资料,可见《GDB User's Guide》。
Tornado 的 Debug 菜单提供了调试器命令的全部列表,如常见的 Step into、Step Over、
Step out 和 Continue 等;使用键盘快捷方式也可执行相应的命令,表 1-2 描述了这些菜单
的具体功能。同时,在 CrossWind 工具栏中也提供了按钮(如图 1-17 所示),它们可执行
一些较常用的调试命令。
表 1-2 调试器命令列表
菜 单 功 能
SourceSearchPath 设置源代码的搜索路径
Detach 将一个在调试器控制下的任务从调试器任务环境中分离
Attach 将一个指定的任务绑定到调试器控制中
Breakpoints 打开断点列表窗口
StepInto 单步方式执行代码的下一行。如果该语句是子程序调用,则进入到函数体内部
StepOver 单步方式执行代码的下一行。如果该语句是子程序调用,则将该子程序执行完
StepOut 继续执行程序,直到当前子程序返回
图 1-20 运行下载的程序
图 1-21 调试源代码窗口
图 1-24 单步执行后的目标仿真器输出窗口
程序清单 1-1 非常简单,上面的过程只是调试过程的一个简单示例,读者可在此基础上
对其他的调试器功能进行深入的了解。
1.5 目标机浏览器
用目标机浏览器(Browser)可查看内存分配情况、任务列表、任务堆栈的使用情况、
CPU 利用率以及系统目标(如任务、消息队列、信号量等)信息等。对这些信息可周期性地
进行更新。
图 1-25 目标机浏览器
图 1-26 内存使用情况
图 1-27 模块信息
通过浏览器可以分析出目标机的一些错误,常见的问题有如下几种:
1. 内存泄漏
图 1-26 中,单击 Toggle Refresh 按钮进行周期刷新,如果发现存储器中的分配持续增
长,那么可能是由内存泄漏引起的。在一个运行的系统中,内存分配的持续增长是必须避免
的,因为长时间的运行必将导致系统存储器耗尽,从而使系统产生一些不可预知的结果。
2. 存储器碎片
当长时间对小块内存和中等大小的内存进行交叉分配,而这些小块内存又没有被及时释
放时,将会产生大量的内存碎片;而内存碎片的产生会降低系统的性能。此时借助于浏览器
工具可以很好地分析该问题。如果在释放列表中存在许多小块节点,而且这些节点数目呈增
长趋势,则可断定出现了过多的内存碎片。如图 1-28 所示,在释放列表中只包含 3 个节点,
但在图 1-29 中,就出现了很多小块内存。
图 1-28 系统的内存分区信息(无内存碎片)
图 1-29 系统的内存分区信息(存在内存碎片)
3. 堆栈溢出
堆栈溢出也是程序设计中的一个大问题。一个任务的堆栈溢出后,会对其他程序产生一
些影响,从而使整个系统的行为变得难以解释。当遇到这种问题是,可以借助于堆栈检查工
具来验证是否存在堆栈溢出。例如:在 test.c 文件中添加程序清单 1-2 所列代码。
程序清单 1-2 堆栈溢出测试代码
void task0() {
char test[4096];
getchar();
}
重新编译后下载到目标系统中,然后在 WindSh 下执行 task0 并打开浏览器的 Stack
Check 界面显示新生成的任务(图 1-30 中的 t3),此时它的堆栈使用情况如图 1-30 所示。
图 1-30 堆栈信息(无溢出)
图 1-31 堆栈信息(堆栈溢出)
假设有三个不同优先级的任务 s1u10(低优先级)、s1u11(中优先级)、s1u12(高优
先级),如果任务 s1u10 和 s1u12 使用同一个信号量来访问共享资源,而 s1u11 无需使用共
享资源,s1u10 任务获得权限后开始对共享资源进行访问,此时如果任务 s1u11 也开始运行,
由于 s1u11 的优先级高于 s1u10,所以就象图 1-32 显示的一样它独占 CPU,最后导致 s1u10
无法释放信号量,s1u12 也无法运行,产生了优先级反转的现象。
一旦发生了优先级反转,我们一方面可以使用 SPY 来检查 CPU 的占用情况,同时可以检
查那些具有高优先级而处于挂起状态的任务,检查挂起的具体信号,再通过 Object
information 查看该信号被哪个任务所占用,分析是否是产生了优先级反转,并根据分析的
结果对程序做适当调整。
1.6 软件逻辑分析器
图 1-34 选择事件源
图 1-36 软件逻辑分析图
从图 1-36 可清楚地看出各个任务的执行情况以及信号量的等待情况,图中有一些有用
的符号(如 、 等),将鼠标停留在这些符号上时,系统会自动的给出相应的提
示。