简介:在VxWorks嵌入式开发中,BootRom是VxWorks操作系统集成的启动代码,在Tomado集成开发环境的Build菜单中可以直接编译BootRom,它的功能和U-boot类似,都是Bootloader程序。只是BootRom是由VxWorks提供的一个bootloader程序,通过它可以和Tornado集成的一些工具进行VxWorks内核的下载和调试工作。
通常,BootRom软件有以下功能:
(1)通过串口下载操作系统映像;
(2)通过串口升级自身映像;
(3)通过串口下载系统配置文件、系统信息文件;
(4)加载操作系统映像,使其正常启动;
(5)其他的辅助功能,如地址内容查看功能、地址内容修改功能和bootrom菜单显示信息控制功能
1、usrTffsConfig (0, 0, "/kernel" );
vxworks的tffs位于dosfs文件系统和底层硬件中,目的是dosfs文件系统是针对磁盘的FAT文件系统,而大部分嵌入式存储设备是flash,flash和磁盘在物理特性上差别很大,读写方式不同:磁盘是一个一个字节修改,而flash在写之前必须擦除,其擦除和写有次数限制(大部分是10W次);tffs屏蔽了底层设备差异。
dosfs位于tffs上层,包含翻译层、MTD层和socket层。翻译层由vxworks提供,不需要修改,我们只需要修改MTD层和socket层。
翻译层主要实现TrueFFS和dosFs之间的高级交互功能,也包含了控制flash映射到块、wear-leveling、碎片回收和数据完整性所需的智能化处理功能。
MTD层:即设备驱动层,包含flash读、写、擦除、ID识别等驱动。
socket层:用来提供tffs和板卡硬件(如flash卡)的接口服务。
2、VXWORKS的BSP开发方法
一般分为公共特性的BSP开发和特有特性的BSP开发。
公共特性的开发对平台来说是各个产品需要做适配的工作,并且不需要修改或者修改很少的vxworks内核代码,在内核配置中增加需要相应的特性即可。但增加特性后,会在BSP目录下的config.h文件增加INCLUDE宏;并且在usrboot()即内核启动程序中添加相应的初始化函数。例如增加tffs特性:在内核配置tffs后,config.h会增加INCLUDE_TFFS宏,并自动在prjconfig.C文件(此文件也是根据配置生成的)的usrIosExtraInit()函数增加tffsDrv()和usrTffsConfig()函数。
特有特性的BSP开发是各个产品负责的,每个产品由于硬件配置不一样,所以BSP特性也需要做相应的改变。这种特有特性的开发则需要将和BSP相关的.c和.h文件放在自己的BSP目录下,修改产品的初始化代码,添加相关的BSP初始化(取代公共特性开发中vxworks自动添加的步骤)。例如增加tffs特性:不需要配置内核支持tffs特性,取代的是将systffs.c、systffs.h放在BSP目录下,在初始化中添加tffsDrv()和usrTffsConfig()函数。
3、TFFS中两大数据结构:FLSOCKET和FLFLASH
FLFLASH是MTD层维护数据的结构:结构中包含了MTD维护的数据和flash的操作方法,包括重映射、读、写、擦除等函数指针。这个数据结构包含socket指针,指向flash对应的socket数据结构。
FLSOCKET是socket层维护的数据结构:为TFFS提供了指向处理flash硬件接口的函数指针。
4.用usrTffsConfig()给flash挂接dos设备
用usrTffsConfig(drive,removable,fileName)在tffs flash驱动设备上挂在文件系统。它的参数如下:
drive:制定tffs flash驱动设备的驱动号,有效值是从0到BSP中套接字接口数量。
Removable:指定是否为可移动设备。固定的为0,可移动的为1.
fileName:指定挂载点,例如,”/tffs0/”.
在shell中输入tffsDevFormat(0,0) 成功后,继续输入usrTffsconfig(0,0,”/tffs0/”),返回value=0=0x0,成功。
此时用 devs 命令查看挂接的设备名,发现 /tffs0/ ,说明设备挂接成功。接下来就可以使用 dosfs 文件系统相关命令操作 flash 设备了,如 ls,copy 等。VxWorks的设备驱动程序有三张表来维护: Fd Table、Dev Table、Drv Table,分别表示文件描述符表、设备列表、驱动程序表。
调用open、read、write、ioctl这些函数时都要使用一个句柄即文件描述符,即fd Table(文件描述符表)。在终端模式下用命令iosFdShow函数查看已使用的文件描述符内容:
->iosFdShow
fd name drv
3 /pcConsole/0 2
4 /vio/1 7
dev tabel中维护系统中所有的设备,在命令行中敲入devs便可以查看dev Table的内容,就是设备列表:
->devs
drv name
0 /null
1 /tyCo/0
1 /tyCo/1
2 /pcConsole/0
2 /pcConsole/1
4 /ata0a
6 host:
7 /vio
8 /ghDev
9 xxdev
dev table中包含两个内容:一个是驱动号(drv),一个是设备名称(name),仔细对比上面的dev tabel和fd table就可以发现,这两个表中设备名称和驱动号是对应着的。
Drv table ,驱动程序表,维护者所有的驱动程序,在命令行里:
-> iosDrvShowdrv create delete open close read write ioctl
1 388660 0 388660 3886a0 388eb0 388da0 3886e0
2 30bae0 0 30bae0 0 388eb0 388da0 30baf0
3 0 0 384040 3840a0 3840d0 384120 384240
4 36d3f0 36d7f0 36d030 36d490 36e180 36e1f0 36eb60
5 0 0 0 33e970 33f8b0 33f2d0 356ec0
6 348e80 349380 3499e0 349ec0 34c2f0 34c640 34c870
7 3918c0 0 3918c0 3919a0 388eb0 388da0 391a40
8 318620 0 318620 3186e0 318650 318680 3186b0
9 318840 318850 318870 3188a0 318900 318930 3188d0
从上面的表中可以看到驱动号(drv)和七个函数。驱动号是驱动程序的一个索引号,这个索引将三张表联系起来,任何一张表都能够通过这个索引找到彼此;Drv table后面有七个驱动函数,下面所列的数字是这些函数的地址。
例如:
open的第一个参数是一个设备名,系统就会首先在dev Table设备列表中寻找该设备名"/ghDev",找到之后就知道了这个设备的驱动号8,8号设备找到了怎么执行相应的open函数呢,这时候系统找到Drv table中与8号对应的驱动程序们的地址,找到与open对应的函数的入口地址318620,便将程序跳转至318620处执行。执行完后为这个已经open成功的8号设备"/ghDev"分配一个fd,并添加到fd table中去,便于应用程序继续使用该设备。在执行完open之后,接着就要对设备进行各种read\write操作,同样在drv tabel中找到相应的入口函数执行。
两个疑问:
1、怎样把我的open函数、wrie函数等加到Drv Table中去呢?
2、怎么让系统把我自己的设备也列入到dev table设备列表中呢?
答案就是两个函数:iosDrvInstall()、iosDevAdd()。
/dev/null是个黑洞设备 ,它丢弃一切写入其中的数据,空设备通常被用于丢弃不需要的输出流。任何写入该设备数据都会被丢弃掉。从这个里面读取数据返回空(也有人认为是读该空设备,直接读到文件尾,那就是返回-1)。将一些不用内容经常发送给这个设备,丢弃不需要的数据。5、taskSpawn 创建并激活一个新的任务
int taskSpawn
(
char *name, /*任务名*/
int priority, /*任务优先级,vxWorks好像共255个,而且调度采用优先级抢占式,同优先级轮换式的调度方式*/
int options, /*任务的一些特性,例如VX_SUPERVISOR_MODE 0x0001 OBSOLETE: tasks always in sup mode*/
int stackSize, /* 需要申请堆栈的大小*/
FUNCPTR entryPt, /*任务处理函数*/
int arg1, /*任务处理函数需要的参数*/
int arg2,
int arg3,
int arg4,
int arg5,
int arg6,
int arg7,
int arg8,
int arg9,
int arg10
)
使用i查看系统中的任务。
一般来说,应用程序的优先级不应当比系统任务高。系统默认任务优先级是100 最好把用户任务优先级设置成100开外。
堆栈大小一般是根据你程序的内存使用情况而定的,如果拿不准,可以预设大一些,然后根据运行情况(Tornado里有插件可以时时查询)再降低。一般要有50%的余量。
你可以先设置一个比较大的数,然后根据运行后的情况再减小栈,shell里可以查看到栈使用的峰值,你可以在这个基础上考虑20%的冗余。
如果栈溢出,那么和内存越界操作是等同效果的。
6、任务的状态
就绪(READY):任务只需要等待CPU资源;
阻塞(PEND):由于CPU以外的资源不可用而阻塞;
睡眠(DELAY):任务处于睡眠状态;
挂起(SUSPEND):这种任务状态不能执行
DELAY+S既处于睡眠状态又处于挂起状态:挂起异常的任务(默认的异常处理)
PEND+S既处于阻塞状态又处于挂起状态
PEND+T超时阻塞
PEND+S+T超时阻塞并挂起
State+T处于state带有一个继承优先级的任务状态。
7、控制任务调度的函数调用
kernelTimeSlice()控制轮转调度:参数为时间片的长度,即每个任务放弃CPU给另一个同优先级的任务前,系统允许它运行的最大长度。
taskPrioritySet()改变任务的优先级
taskLock()禁止任务调度
taskUnlock()允许任务调度
wind内核的优先级 256个,编号0~255,优先级0最高,255最低。
任务的优先级在创建时指定,任务可以调用taskPrioritySet改变自己的优先级。
8、轮转调度:让优先级相同的、处于就绪态的任务公平的共享CPU。轮转调度使用时间片来分配CPU,每个任务执行一个预先确定的时间段(即时间片)。
使用轮转调度算法时,每个任务都有一个运行时间计数器
随着系统时钟增加而增加,达到规定的值(时间片的值)时,清0,此时,任务放到所在优先级队列的尾部。
一个新加入的任务放在所属优先级队列的尾部,计数器初始为0.当被高优先级任务抢占时,保存它的当前运行时间计数器,下次被调度时,恢复这个值。
9、抢占上锁
在实际应用中,有时候需要避免抢占,以免发生不合理的抢占或者发生一些意想不到的情况。wind的调度器提供:
taskLock()和taskUnlock()来禁止/允许抢占。
禁止:当该任务执行时,将不会发生基于优先级的抢占;
只能防止任务的上下文切换,不能禁止中断;
禁止抢占可以用来实现互斥;但应当尽量使禁止抢占时间最小。
vxworks动态链接功能很容易实现代码共享,一个被多任务调用的单个备份称为共享代码。
共享代码必须是可重入的:被多个任务同时调用而不会发生冲突。
惯例:
所有name_r()命名的子程序被认为是不可重入的;
vxworks的I/O驱动程序是可重入的。
编写可重入代码的技术:
使用动态堆栈变量;
使用信号量保护全局或静态变量;
任务变量。
10、vxworks系统根任务:
usrRoot是内核执行的第一个任务。prjConfig.c文件中有usrRoot()函数。
vxwork提供丰富的任务控制功能,包含在taskLib库中。包括任务的创建、控制和获取任务信息。
11、taskSpawn
堆栈是系统资源,位于内存中,其底端是TCB(任务控制块)和任务名,堆栈使用0xEE填充。
成功返回任务ID号:四字节
失败返回ERROR:
s_intlib_not_isr_callable程序不能从一个ISR中调用;
s_objlib_obj_id_error不正确的任务ID
s_memobjlib_not_initialized在指定的分区中,没有足够的内存用于发起任务
s_memlib_block_error不能对内存分区互斥访问
s_tasklib_illegal_priority非法的优先级
任务名约定:所有从目标机启动的任务以字母t开头命名,从主机启动的任务以字母u开头命名。
若没有任务名,则vxworks为其分配一个独一无二的名字tN,N是随未命名任务数递增的一个十进制整数。
12、任务运行的控制
taskSuspend()挂起任务
taskResume()恢复任务
taskRestart()重启任务
taskDelay()任务延时,单位为tick,将会导致任务呗移到相同优先级就绪队列的尾部。例如taskDelay(0)可以将CPU交给同优先级的另一个任务。
nanosleep()任务延时,单位为纳秒,参数不能是0.
13、POSIX和wind调度方法的差异:
不同:
POSIX调度基于进程,而WIND调度基于任务。
进程与任务的不同:
任务可以直接访问内存,而进程不可以;
进程仅仅继承了父进程的特定特征,而任务则操作在与父任务完全一样的环境中。
进程与任务的相似之处:
任务和进程都可以被单独调度。
POSIX与wind的优先级编号方案不同:
POSIX的优先级数大,优先级高;
wind的优先级数大,优先级低。