VxWorks基础培训
概 念 VxWorks是WindRiver公司开发的具有工业领导地位的高性能实时操作系统内核,具有先进的网络功能。 VxWorks的开放式结构和对工业标准的支持,使得开发人员易于设计高效的嵌入式系统,并可以很小的工作量移植到其它不同的处理器上。
VxWorks内核—Wind VxWorks是带有一个相当小的真正微内核的层次结构。内核仅提供多任务环境、进程间通信和同步功能。这些功能模块足够支持VxWorks在较高层次所提供的丰富的性能的要求。 多任务内核、任务机制、任务间通信和中断处理机制是VxWorks运行环境的核心。
多任务 一般的应用被转换成相互独立又相互协作的程序,每一个程序在运行时称作一个任务。 VxWorks的实时内核wind提供了一个基本的多任务环境。内核按照一种调度算法交替运行各个任务,每一个任务都有自己的上下文。在一次上下文切换中,一个任务的上下文被存在任务控制块(TCB)中。
任务状态 任务状态反映任务当前在系统所处的情形。在VxWorks中,任务有几种状态 :就绪(READY)、阻塞(PEND)、睡眠(DELAY)、挂起(SUSPEND)及它们的组合DELAY+S、PEND+S、PEND+T、PEND+S+T、State+I。
任务状态转换 Wind微内核的状态迁移表如下图所示:
Wind内核的任务调度 多任务处理需要一个调度法则对CPU准备运行的任务进行分配。对于wind内核来说,基于优先级的抢占调度方式是系统的默认工作方式。当然,也可以根据应用程序的需要选择时间片轮转的调度方式。 任务的调度控制程序有: -taskPrioritySet( ) 改变一个任务的优先级 -taskLock( ) 禁止任务的重调度 -taskUnlock( ) 允许任务的重调度
优先级式的抢先调度 每一个任务都有一个优先级,内核则确保首先把CPU分配给优先级最高的一个待运行的任务,这个调度的策略就是一个优先级别较高的待运行任务可以抢先运行,内核会立即存储当前正在运行的任务的上下文并切换到高优先级的任务的上下文。 Wind内核一共有256个优先级别,从0到255,0优先级最高,255最低,任务在建立的同时也分配了一个优先级。 当任务执行时可以调用taskPrioritySet()动态改变自己的优先级。
基于优先级的抢占调度示意图 在运行过程中也可以使用taskPrioritySet()函数改变优先级
时间片轮转式调度 基于优先级的抢占式调度可以与轮转调度相结合。 轮转式调度是指在同一优先级别上的就绪任务可以公平地共享CPU,每一个任务可以运行一个确定的时间片,然后循环运行其它的任务,每个任务都是相同的时间片。
基于优先级的抢占式调度与轮转调度算法相结合调度示意图
抢占上锁 Wind的调度器可以通过tasklock( )和taskUnlock( )对一个任务锁定或允许抢先调度。当一个任务通过调用taskLock( )来锁定抢先调度,在任务的运行期间就避免了高优先级的任务的抢占。
VxWorks任务编程接口 在VxWorks中的taskLib可以得到有关任务的基本例程。这些程序提供了任务的创建、控制和和获取任务信息。
任务的创建和激活 函数调用 描 述 taskSpawn( ) 产生并激活一个新的任务 taskInit( ) 初始化一个新的任务 函数调用 描 述 taskSpawn( ) 产生并激活一个新的任务 taskInit( ) 初始化一个新的任务 taskActivate( ) 激活一个已经初始化的任务
taskSpawn()的参数包括新任务的名称(ASCII码串),优先级,选项,堆栈大小,主程序地址,和10个可以传递给主程序的起始参数。 调用示例: id=taskSpawn(name,priority,option, stacksize,entFunc,arg1,…arg10); taskSpawn()例程屏蔽了低层次的分配、初始化、激活步骤。初始化、激活功能由taskInit()和taskActivate()函数提供,然而,建议只有当需要在分配或激活上进一步控制的时候才使用他们。
任务信息 taskInfoGet( ) 获得一个任务的信息 taskPriorityGet( ) 检查任务的优先级 taskRegsGet( ) 检查任务的寄存器 taskregsSet( ) 设置一个任务的寄存器 taskIsSuspended( ) 检查任务是否处于悬挂状态 taskIsReady( ) 检查任务是否就绪 taskTcb( ) 获得任务控制块的指针 由于任务的状态是动态的,获取信息前必须挂起当前任务。
任务控制 taskSuspend() 悬挂一个任务 taskResume() 恢复执行一个任务 taskRestart() 重启一个任务 taskDelay() 任务延时,单位为tick nanosleep() 任务延时,单位为纳秒
VxWorks系统任务 根任务: tUsrRoot 日志任务: tLogTask 异常处理任务: tExcTask 网络任务: tNetTask 目标代理任务: tWdbTask 可选组件的任务
任务间通信 任务间通信机制是多任务相互同步和通信以协调各自活动的主要手段。 VxWorks提供了几种方法: - 共享内存,用于简单的数据共享; - 信号量,用于基本的互斥和同步; - 消息队列和管道,CPU内任务间的消息传递; - Sockets & RPC,实现网络透明的任务间通信; - Signals,用于异常处理。
共享内存访问互斥 共享一个地址空间,能够简化数据交换。为避免争夺,需要对内存的访问上锁以保证访问互斥进行。实现资源互斥访问的方法: - 禁止中断 - 禁止抢先 - 使用信号量
信号量 VxWorks信号量是提供任务间通信、同步和互斥的最优选择,提供任务间的最快速通信。 对于互斥,信号量可以上锁对共享资源的访问。并且比禁止中断或禁止抢占提供更精确的互斥粒度。
信号量的种类 二进制:最快最通用的信号量,用于同步和互斥。 互斥:一种特殊的二进制信号量,对互斥固有的问题进行了优化:优先级继承,删除安全性,递归。 计数器,类似 二进制信号量, 但是跟踪给定信号量的次数,优化用于一个资源的多个实例。
信号量控制 semBCreate() 分配并初始化一个二进制信号量 semMCreate() 分配并初始化一个互斥信号量 semCCreate() 分配并初始化一个计数器信号量 semDelete() 终止并释放一个信号量 semTake() 取一个信号量 semGive() 释放一个信号量 semFlush() 解锁所有等待该信号量的任务
二进制信号量 一个二进制信号量可认为是一种标记:对应资源是“可获得”(满)还是“不可获得”(空)。 如果信号量是满,在任务取之后变成空,任务可继续执行,如果信号量是空,则任务排到阻塞队列中进入悬挂状态。 可以满足两种类型的任务协调需要:互斥和同步。
互斥信号量 互斥信号量是一种特殊的双态信号量,它用于解决某些互斥中的内在问题,包括: 优先级倒置 删除保护 对资源的递归调用。
互斥信号量有一个SEM_INVERSION_SAFE的参数,可以实现优先级继承的算法。优先级继承协议允许访问同一临界资源的一组任务中,当前运行的任务可继承任务组中的最高优先级。从而解决了优先级倒置的问题。
删除安全 互斥中的另一个问题涉及到任务的删除。删除一个在临界资源中的正在执行的任务往往会带来严重的错误。 互斥信号量提供了一个SEM_DELETE_SAFE的参数使得每个semTake()都含有taskSafe(),且每个semGive()都含有taskUnsafe()。通过这个方法,一个持有信号量的任务就可以得到删除保护。
计数器信号量 计数信号量与双态信号量的唯一区别在于它跟踪信号量GIVE的次数。 计数信号量用于保护具有多个复制的资源。
消息队列 在VxWorks中,单个CPU中任务之间的通信主要是由消息队列完成。 Wind提供按FIFO排队的消息队列,但有一个例外,Wind消息队列有两个优先级: 正常 MSG_PRI_NORMAL 紧急 MSG_PRI_URGENT
消息队列控制函数 msgQCreate() 分配并初始化一个消息队列 msgQDelete() 终止并释放一个消息队列 msgQSend() 向一个消息队列发送消息 msgQReceive() 从一个消息队列接收消息
管道 Pipes提供一个穿过I/O系统的消息队列设备接口。 Pipes提供Message Queue的消息排队和阻塞机制,并能提供一个独特的能力:与Select函数一起使用。通过Select(),Pipes允许Task等待接收来自一个I/O设备集合的数据。 产生一个Pipes: status = pipeDevCreate ("/pipe/name", max_msgs, max_length);
网络的任务间通信 VxWorks通常使用以太网作为传输媒介。在以太网之上,VxWorks提供了几种网络工具: -套接字(Socket) -远程调用(RPT)
套接字(Socket) VxWorks中,网络上任务间的通信就是套接字sockets。一个套接字就是任务间通信的终点,数据从一个套接字传到另一个套接字上,当一个套接字创建后,就已经规定了网络通信中数据传输的协议。 VxWorks支持Internet中的TCP和UDP两种协议。
信号(Signals) VxWorks提供一个软信号机制来异步地改变任务的执行流程。Signals可以被理解为一个软中断,不过与ISR不同的是:Signals是在任务下一次调度运行的时候执行,而ISR可以随时打断Task的执行。
中断服务程序 VxWorks的中断服务程序(ISRs)运行在任务上下文之外的一个特殊的上下文中。因此,中断处理不包括任务的上下文切换。 VxWorks提供函数intConnect()将指定的C函数与任意中断相联系。
中断与任务间的通信 信号量:ISRs可以GIVE信号量而任务可以TAKE或等待 信号(SIGNALS):ISRs可以“指示”任务导致任务的信号句柄的异步调度
VXWORKS程序特点 与WINDOWS,UNIX编程不同,在VXWORKS下的应用程序代码一般是和VX内核代码一起编译连接生成一个映象。 Vxworks操作系统通过BSP(板级支持包)来支持不同硬件平台。
三种不同的VxWorks映象比较 VxWorks映象通常由三个部分组成:BSP、操作系统内核和上层应用。从文件的组织形式上可以将映象分为三段:BSS段、Data段和Text段。其中,BSS段存放的是未初始化的全局变量或静态变量,Data段存放的是已经初始化的全局变量或静态变量,Text段存放的是可执行的代码
根据应用场合的不同,VxWorks映象可分为三类: Loadable images Rom-based images Rom-resident images 其中,Loadable images通过烧写在ROM中的Bootrom装载到RAM中来启动;Rom-based images使用自身带的启动代码把自己装载到RAM中来启动;而Rom-resident images在ROM中启动,只是把需要动态修改的Data段和BSS段装载到RAM中去。
Loadable images(可加载的VxWorks映象) 这是一种运行于RAM的VxWorks映象。它不包含搬移程序,需要借助于一些外部的程序如bootRom才能加载到RAM的低端RAM_LOW_ADRS地址处。这是缺省的开发映象。
图1、可加载的VxWorks映象
基于ROM的VxWorks映象 这是一种运行于RAM中,但起初存放于ROM中的VxWorks映象。即该映象需要和搬移程序一起固化在BOOT中。目标板上电后,首先运行BOOT中的引导搬移程序,将整个VxWorks映象拷贝到RAM地址RAM_LOW_ADRS处,并跳转到此处执行。如图2所示。
该映象根据是否被压缩又可分为: 基于ROM的未压缩的VxWorks映象,可直接从ROM拷贝到RAM中 基于ROM的压缩的VxWorks映象,这种映象主要是为了节约BOOT空间,在从ROM拷贝到RAM的过程中需要解压缩,因此与上述未压缩的映象相比,它的引导过程相对较慢,但两者在RAM中的运行速度是一样的。
图2 基于ROM的VxWorks映象
驻留ROM的VxWorks映象 这种映象起初也和搬移程序一起固化在BOOT中。目标板上电后,首先运行BOOT中的引导搬移程序,但仅将VxWorks映象的数据段和BSS段拷贝到RAM地址RAM_LOW_ADRS处,映象的代码段仍旧留在ROM中,从ROM中开始执行。如图3所示。
图3 驻留ROM的VxWorks映象
Vxworks集成开发环境TORNADO
创建一个VXWORKS简单示例 在tornado中创建一个新的project,它会出现如下对话框 ,bootable Vxwork image 一般用于完整的vxworks映象。 Downloadable application modules 用于一个模块,目标文件一般是 *.a或 *.out 文件 这里选择创建一个 bootable Vxwork image类型的project.
void usrAppInit (void) { #ifdef USER_APPL_INIT USER_APPL_INIT; /* for backwards compatibility */ #endif taskSpawn("tzxc10",60,0,30240,(FUNCPTR)zxc10,0,0, 0,0,0,0,0,0,0,0); } void zxc10(void) ………………….
讨 论
谢谢