810 likes | 1.19k Views
VxWorks 及其应用开发 . 陈香兰 xlanchen@ustc.edu.cn http://staff.ustc.edu.cn/~xlanchen 2006 年 7 月. 课程内容. VxWorks 和 Tornado 简介 任务和任务间通信 信号、中断处理与定时机制 调试环境与实例分析 网络编程 VxWorks 操作系统配置. VxWorks 的任务 与任务编程接口. xlanchen@2006.7.12. 1. VxWorks 的任务 1.1 多任务. 软件设计时,通常将应用划分成独立的、相互作用的程序集合。对于每个程序,当其执行时,我们称之为任务
E N D
VxWorks及其应用开发 陈香兰 xlanchen@ustc.edu.cn http://staff.ustc.edu.cn/~xlanchen 2006年7月
课程内容 • VxWorks和Tornado简介 • 任务和任务间通信 • 信号、中断处理与定时机制 • 调试环境与实例分析 • 网络编程 • VxWorks操作系统配置
VxWorks的任务与任务编程接口 xlanchen@2006.7.12
1. VxWorks的任务1.1 多任务 • 软件设计时,通常将应用划分成独立的、相互作用的程序集合。对于每个程序,当其执行时,我们称之为任务 • VxWorks实时内核Wind提供基本的多任务环境 • 在单CPU系统中,多任务构造出多个线程并发执行的假象 • 系统根据调度算法,调度这些任务运行
任务有自己的上下文 • 包括CPU环境、系统资源等 • 任务由系统内核调度运行 • 上下文切换时,任务的上下文保存在任务控制块(TCB)中 • 一个任务的上下文包括: • 任务的执行点(程序计数器) • CPU寄存器和浮点计数器
动态变量和函数调用的堆栈 • 标准输入/输出以及出错的I/O分配 • 一个延时定时器 • 一个时间片定时器 • 内核控制结构 • 信号处理器 • 调试和性能监视值
与Windows系统不同,VxWorks操作系统的内存是线性的与Windows系统不同,VxWorks操作系统的内存是线性的 • 使用单地址空间 • 所有代码执行在单一、公共的地址空间内 • 因此地址空间不属于任务上下文 • 当选用VxVMI时,每个任务各自的地址空间需要进行虚拟地址到物理地址的转换
1.2 任务状态转换 • 任务状态反映任务当前在系统中所处的情形 • 任务状态由内核负责维护。因此,任务状态的转换是应用调用了某些内核调用的结果
任务的状态 • 就绪(READY) • 阻塞(PEND) • 睡眠(DELAY) • 挂起(SUSPEND) • DELAY+S • PEND+S • PEND+T • PEND+S+T • State+T 任务只需要等待CPU资源 由于CPU以外的资源不可用而阻塞 任务处于睡眠状态 这种任务状态不能执行。主要用于调 试。不会约束状态转换,仅仅约束任 务的执行。因此,pended-suspended任 务仍然可以解锁,delayed-suspended 任务仍然可以唤醒 既处于睡眠状态又处于挂起状态 既处于阻塞状态又处于挂起状态 超时阻塞 超时阻塞并挂起 处于State带有一个继承优先级的任务状态
任务的状态转换图 任务创建时,处于挂起状态 挂起 要使刚创建的任务就绪, 要激活该任务 taskResume() taskResume() taskSuspend() taskSuspend() taskSuspend() taskResume()/taskActivate() 睡眠 阻塞 就绪 taskDelay() 延时已到 使用spawning原语,可以直接 创建并激活一个任务 调度 semTake()/msgQReceive() semGive()/msgQSend() 抢占 执行 任务可以在任何状态下被删除
1.3 Wind任务调度 • 调度是针对多任务而言的 • 调度是指:根据一定的约束规则,将CPU分配给符合条件的任务使用 • 上述约束规则就是所谓的调度算法 • Wind内核默认采用基于优先级的抢占式调度Priority-based preemptive scheduling • 同时还使用轮转(Round-Robin)调度算法
控制任务调度的函数调用 • kernelTimeSlice() • taskPrioritySet() • taskLock() • taskUnlock() 控制轮转调度 改变任务的优先级 禁止任务调度 允许任务调度
基于优先级的抢占式任务调度 • 基于优先级的抢占式任务调度 • 系统中的每个任务都拥有一个优先级 • 任意时刻,内核将CPU分配给处于就绪态的优先级最高的任务运行 • 抢占:一旦内核发现有一个比当前正在运行的任务的优先级高的任务就绪,内核立即保存当前任务的上下文,切换到这个高优先级任务的上下文中运行
Wind内核的优先级 • 256个,编号0~255 • 优先级0最高,255最低 • 任务的优先级在创建时指定 • 任务可以调用taskPrioritySet改变自己的优先级 • taskPrioritySet/Get的使用举例
轮转调度 • 轮转调度通常配合基于优先级的抢占式调度进行 • 轮转调度:让优先级相同的、处于就绪态的任务公平地共享CPU • 轮转调度使用时间片来分配CPU • 每个任务执行一个预先确定的时间段(即时间片)
VxWorks中,调用函数kernelTimeSlice()来使用轮转调度VxWorks中,调用函数kernelTimeSlice()来使用轮转调度 • 参数为时间片的长度(即:在每个任务放弃CPU给另一个同优先级的任务之前,系统允许它运行的最大时间长度)
使用轮转调度算法时,每个任务都有一个运行时间计数器使用轮转调度算法时,每个任务都有一个运行时间计数器 • 随着系统时钟增加而增加 • 达到规定的值(时间片的值)时,清0 • 此时,任务放到所在优先级队列的尾部 • 一个新加入的任务放在所属优先级队列的尾部,计数器初始为0 • 当被高优先级任务抢占时,保存它的当前运行时间计数器,下次被调度时,恢复这个值
抢占上锁 • 在实际应用中,有时候需要避免抢占,以免发生不合理的抢占或发生一些意想不到的情况 • Wind的调度器提供taskLock()和taskUnlock()来禁止/允许抢占 • 当一个任务调用taskLock(),将会禁止抢占,在该任务执行时,将不会发生基于优先级的抢占
注意:禁止抢占只能防止任务的上下文切换,不能禁止中断注意:禁止抢占只能防止任务的上下文切换,不能禁止中断 • 禁止抢占可以用来实现互斥 • 但是,应当尽量使禁止抢占的时间最小
1.4 任务异常处理 • 程序代码或数据的错误可能引起硬件异常状态,例如 • 非法指令 • 总线或地址错 • 除数为0 • 等等 • VxWorks的异常处理包处理这些异常
默认的异常处理 • 挂起引起异常的任务 • 保存该任务在异常点的状态 • 将关于异常的描述送到Tornado开发工具中 • 同时内核和其他任务不会被中断,系统继续执行
定制的异常处理 • VxWorks允许任务使用信号功能激活自己的异常处理程序 • 如果一个任务已经提供了一个异常的信号处理程序,系统的默认处理过程不被执行
1.5 共享代码和重入 • VxWorks提倡:单个子程序的备份或子程序库被多个不同的任务调用 • 如,许多任务会调用printf(),但是系统中只有一个备份 • 定义:一个被多个任务调用的单个备份成为共享代码 • VxWorks动态链接功能很容易实现代码共享
重入 • 共享代码必须是可重入的: • 一个子程序是可重入的,如果该程序的单个备份可以被多个任务同时调用而不会发生冲突 • 典型的冲突: • 对全局或静态变量的修改 • VxWorks的很多子程序是可重入的 • 惯例:所有name_r()命名的子程序被认为是不可重入的 • VxWorks的I/O和驱动程序是可重入的
大部分VxWorks程序使用的重入机制 • 使用动态堆栈变量 • 使用信号量保护全局或静态变量 • 任务变量 • Wind推荐使用上述技术编写可重入代码
动态堆栈变量 • 许多子程序仅仅是纯代码,除了动态堆栈变量外没有自己的数据 • 这些程序除了调用者以参数形式提供的数据之外,不需要其他数据 • 例如lstLib链表操作库 • 它的程序操作由调用者提供lists和nodes • 这种程序只在自己的堆栈内进行操作
受保护的全局/静态变量 • 使用semLib中提供的信号量对全局/静态变量进行互斥保护 • 将在任务间通信中介绍信号量机制
任务变量 • 任务变量机制允许在任务上下文中定义自己的私有变量,随着上下文切换而切换 随着上下文的切换而切换
使用相同主程序的多个任务 • VxWorks可以使用同一个主程序作为几个不同的任务运行,每个任务将使用自己的堆栈和上下文,并分别传给该程序不同的参数 • 可以满足同一个程序需要以不同的参数并发执行的情况,例如 机械臂的多个连接可以使用相同 的代码joint来控制,使用关节号 来指定要操纵的连接
VxWorks的系统任务 VxWorks包含以下系统任务: • 根任务tUsrRoot • 内核执行的第一个任务 • 初始化VxWorks系统的主要功能,例如 • 发起日志任务、异常处理任务、网络任务和tRlogind后台任务 • 通常,上述初始化结束之后,根任务终止并删除 • 用户可以向根任务自由的添加任何必需的初始化代码 • 阅读usrRoot代码
日志任务:tLogTask • 记录系统信息的任务 • 异常处理任务:tExcTask • 提供VxWorks异常处理包,完成在中断级不能执行的功能 • 必需具有系统最高的优先级 • 不需要挂起、删除、改变其任务的优先级
网络任务:tNetTask • 后台处理VxWorks网络需要的任务级功能处理 • 目标代理服务:tWdbTask • 当目标代理设置为运行在任务模式时,创建此任务 • 处理来自Tornado目标服务的请求 • 其他可选组件的任务 • tShell • tRlogind • tTelnetd • tPortmapd
2、VxWorks任务编程接口 • VxWorks提供丰富的任务控制功能,包含在taskLib库中 • 包括: • 任务的创建、控制和获取任务信息 • 可以在Tornado IDE提供的shell工具中交互使用这些调用
taskSpawn():创建并激活一个新任务 int taskSpawn ( char *name, /*新任务的任务名*/ int priority, /*新任务的优先级*/ int options, /*任务选项字*/ int stackSize, /*堆栈大小*/ FUNCPTR entryPt, /*新任务的入口函数*/ int arg1,arg2,arg3,arg4,arg5, /*传递给入口函数*/ int arg6,arg7,arg8,arg9,arg10 /*的10参数*/ )
在源代码中使用TaskSpawn创建新任务 • 在Tornado Shell中 • 使用TaskSpawn创建新任务 • 使用i查看系统中的任务(演示)
TaskSpawn() • 创建新任务的上下文 • 堆栈、含有特定参数的入口程序调用的任务环境(入口程序通常是一个普通的程序) • 新的任务将在指定的函数入口处执行 • 任务名要便于记忆,可以是任意长度、任意内容 • 堆栈是系统资源,位于系统内存中 • 其底端是TCB和任务名 • 堆栈使用0xEE填充
堆栈示意 栈底 高地址 TCB 低地址
taskSpawn()返回值 • 成功:任务ID号 • 失败:ERROR返回的ERRNOS可能有: • S_intLib_NOT_ISR_CALLABLE, /*程序不能从一个ISR中调用*/ • S_objLib_OBJ_ID_ERROR, /*不正确的任务ID*/ • S_smObjLib_NOT_INITIALIZED, /*在指定的分区中,没有足够的内存用于发起任务*/ • S_memLib_NOT_ENOUGH_MEMORY, /*没有足够的内存发起任务*/ • S_memLib_BLOCK_ERROR, /*不能够对内存分区互斥访问*/ • S_taskLib_ILLEGAL_PRIORITY /*非法的优先级*/
初始化、激活一个任务 • taskSpawn()中包含了一些低级的操作,包括初始化和激活一个任务 • taskInit() • taskActivate()
taskInit()初始化一个任务 STATUS taskInit ( WIND_TCB *pTcb, /*新任务的TCB地址*/ char *name, /*新任务的名字*/ int priority, /*新任务的优先级*/ int options, /*任务选项字*/ char *pStackBase, /*任务堆栈基地址*/ int stackSize, /*堆栈大小*/ FUNCPTR entrypt, /*任务入口*/ int arg1,arg2,arg3,arg4,arg5, /*入口参数*/ int arg6,arg7,arg8,arg9,arg10 ) 允许不指定任务名,由系统自动命名 初始化指定的内存区域作为任务的堆栈 和控制块,而不像taskSpawn()那样由系 统自动分配 可以方便应用进行调试,并将堆栈分配 在指定位置
taskInit的返回值: • 成功:OK • 任务不能初始化则:ERROR可能返回的ERRNOS值有:S_intLib_NOT_ISR_CALLABLE /*程序不能从一个ISR中调用*/ S_objLib_OBJ_ID_ERROR /*不正确的任务ID*/