第二章VxWorks基础1任务产生Table2-3:TaskCreationRoutinesCallDescriptiontaskSpawn()Spawn(createandactivate)anewtask.taskInit()Initializeanewtask.taskActivate()Activateaninitializedtask.id=taskSpawn(name,priority,options,stacksize,main,arg1,arg10),VxWorks习惯用ID号为0的任务作为调用发出的任务?阻塞,是指进程因某个事件不能使程序继续运行下去,如要对某I/O操作时!简单的说:阻塞,指进程在主存中并等待一个事件的完成在虚拟内存中,当主存中没有处于就绪状态的进程时操作系统就把被阻塞的进程换出到磁盘中的挂起队列,挂起的原来就差不多是这样,挂起需要和阻塞或就绪结合,简单的说,就是进程在辅存中并等待一个事件。任务控制块(WIND_TCB)多任务设计能随时打断正在执行着的任务,对内部和外部发生的事件在确定的时间里作出响应。VxWorks实时Wind内核提供了基本的多任务环境。从表面上来看,多个任务正在同时执行,实际上,系统内核根据某一调度策略让它们交替运行。系统调度器使用任务控制块的数据结构(TCB)来管理任务调度功能。任务控制块用来描述一个任务,每一任务都与一个TCB关联。TCB包括了任务的当前状态、优先级、要等待的事件或资源、任务程序码的起始地址、初始堆栈指针等信息。调度器在任务最初被激活时以及从休眠态重新被激活时,要用到这些信息,TCB使多个任务得以独立运行,如表1-1所示任务控制块TCB。表1-1任务控制块TCB项目内容和含义任务名称指针指向表示任务名称的字符串上下文程序计数器,表示任务被中止时的位置CPU状态,包括各种处理器特定的寄存器栈,用于动态变量,函数调用,信号处理等标准输入,标准输出,标准错误输出定义延迟定时器,用于任务延迟计数时间片定时器,用于Round-Robin调度内核控制结构(KernelControlStructures)信号处理设置信息,包括阻塞信号集,信号处理方式,信号状态等错误状态,每个任务都有一个独立的全局errno的副本调试和性能监视状态任务变量(可选)浮点上下文(可选)异常信息因处理器体系不同而有差异,用于异常处理退出码exitCode作调试之用为了便于调试,每个任务都有一个独一无二的字符串表示的名称,在任务被创建时由用户程序指定或者系统默认生成。几乎所有的任务控制函数都采用任务ID(等于TCB地址)表示一个任务。VxWorks提供任务名称和任务ID之间的转换函数。TCB的一个重要内容就是任务上下文(ContExT),代表了任务运行状态。VxWorks的任务切换就是将当前任务(被换出CPU)的上下文保存到该任务的TCB,然后从调度程序(Scheduler)选择新任务(被换入CPU)的TCB中恢复上下文。任务上下文:多任务为应用程序对多个离散的现实事件的控制和反应提供了基本的机制。VxWorks的实时内核wind提供了一个基本的多任务环境。内核按照一种调度算法交替运行各个任务,造成一种多个任务并行运行的假象,每一个任务都有自己的任务上下文。任务上下文是任务每次被调度运行时所能看到的CPU环境和系统资源。在一次上下文切换中,一个任务的上下文被存在任务控制块(TCB)中。一个任务的上下文包括:l一个用于执行的线程,即任务的程序计数器lCPU的寄存器和可选择的浮点寄存器l用于动态变量和函数调用的堆栈l对标准的输入、输出、出错的I/O口的分配l延时(休眠)时钟l时间片时钟l内核的控制结构l信号句柄l调试和性能监视参数在VxWorks中,一个非常重要但并不是任务上下文的一项资源就是存储器地址空间:所有代码(任务)都在一个单一的公有的地址空间运行,这一点是区别于许多非实时操作系统的(UNIX,Windows)。2option当任务生成后,一个选项参数一定要给出。若生成一个任务,涉及到浮点操作,则如下定义:tid=taskSpawn(tMyTask,90,VX_FP_TASK,20000,myFunc,2387,0,0,0,0,0,0,0,0,0)Table2-5:TaskOptionsNameHexValueDescriptionVX_FP_TASK0x8Executewiththefloating-pointcoprocessor.VX_NO_STACK_FILL0x100Donotfillstackwith0xee.VX_PRIVATE_ENV0x80Executetaskwithaprivateenvironment.VX_UNBREAKABLE0x2Disablebreakpointsforthetask.选项还可以被检查是否合法Table2-6:TaskOptionRoutinesCallDescriptiontaskOptionsGet()Examinetaskoptions.taskOptionsSet()Settaskoptions.3获取任务信息当一个任务被调度的时候,可以检查当时该任务的上下文,获取该任务的信息。Table2-7:TaskInformationRoutinesCallDescriptiontaskIdListGet()FillanarraywiththeIDsofallactivetasks.taskInfoGet()Getinformationaboutatask.taskPriorityGet()Examinethepriorityofatask.taskRegsGet()Examineatask'sregisters.taskRegsSet()Setatask'sregisters.taskIsSuspended()Checkifataskissuspended.taskIsReady()Checkifataskisreadytorun.taskTcb()Getapointertotask'scontrolblock.4删除任务当一个任务被删除之前,一定要确保该任务释放了所有占有的共享资源。Table2-8:Task-DeletionRoutinesCallDescriptionexit()Terminatethecallingtaskandfreememory(taskstacksandtaskcontrolblocksonly).1taskDelete()Terminateaspecifiedtaskandfreememory(taskstacksandtaskcontrolblocksonly).*taskSafe()Protectthecallingtaskfromdeletion.taskUnsafe()UndoataskSafe()(makethecallingtaskavailablefordeletion).任务删除存在的安全隐患:一个任务可以占有一个信号灯,来占有临界资源;当这个任务被任务删除时,占用的临界资源没有被释放,其他任务就无法得到该临界资源。因为该任务无法释放信号灯。使用tasksafe()和taskUnsafe()可以解决这个问题。前者可以使自己不被其他任务删除;后者其他任务被允许删除自己。典型用例如下:taskSafe();semTake(semId,WAIT_FOREVER);/*Blockuntilsemaphoreavailable*/..criticalregion.semGive(semId);/*Releasesemaphore*/taskUnsafe();5任务控制当调试阶段,需要对任务进行控制,使其挂起,延迟等等。Table2-9:TaskControlRoutinesCallDescriptiontaskSuspend()Suspendatask.taskResume()Resumeatask.taskRestart()Restartatask.taskDelay()Delayatask;delayunitsareticks.nanosleep()Delayatask;delayunitsarenanoseconds.6任务扩展结构这里安排了根任务相关的调用例程指针。当任务被创建,延时,调用等等,由这里指定的例程入口开始执行。Table2-10:TaskCreate,Switch,andDeleteHooksCallDescriptiontaskCreateHookAdd()Addaroutinetobecalledateverytaskcreate.taskCreateHookDelete()Deleteapreviouslyaddedtaskcreateroutine.taskSwitchHookAdd()Addaroutinetobecalledateverytaskswitch.taskSwitchHookDelete()Deleteapreviouslyaddedtaskswitchroutine.taskDeleteHookAdd()Addaroutinetobecalledateverytaskdelete.taskDeleteHookDelete()Deleteapreviouslyaddedtaskdeleteroutine.7错误码全局变量errno用来保存当前执行任务的错误状态;任务上下文切换时,errno会保存到任务的TCB中;底层函数根据情况设置errno,上层调用函数检查errno的值确定函数调用失败的原因;错误码格式:模块编码(16bits)+错误类型(16bits);由错误码获取描述信息:shell命令printErrno,函数strerror();定义用户自己的错误码;0---500号由系统使用;用户从501-----左移16位都可用。8任务异常处理当任务执行出现错误时,比如硬件指令异常,地址越界,0做除数时,异常处理包会处理这些事情。默认做法是,悬挂起异常的任务,其他任务照常。一个任务本身也可以作为硬件异常的处理函数。9共享代码和可重入性。VxWorks的共享代码让系统效率更高,可维护性好,共享代码必需可重入。VxWorks里大多数例程是可重入的。ldiv()hasacorrespondingroutineldiv_r(),前者可重入,后者不可。推荐以下三种方式辅助实现可重入性:odynamicstackvariablesoglobalandstaticvariablesguardedbysemaphoresotaskvariables函数的可重入性(Reentrancy)正确运用在一个多任务环境中,函数的可重入性是十分重要的。可重入函数是一个可以被多个任务调用的过程,任务在调用时不必担心数据是否会出错。在写函数时只要考虑到尽量用局部变量(例如寄存器、堆栈中的变量),对于要使用的全局变量要加以保护(例如采用关中断、信号量等),这样构成的函数就一定是一个可重入的函数。此外,编译器是否有可重入函数的库,与它所服务的操作系统有关,例如DOS下的BorlandC和MicrosoftC/C++等就不具备可重入的函数库,这是因为DOS是一个单用户单任务的操作系统。为了确保每一个任务控制自己的私有变量,在一个可重入的C函数中,须将这样的变量声名为局部变量。C编译器将这样的变量存放在调用栈上或寄存器里。在VxWorks中,多个任务可调用同一子函数或函数库。VxWorks系统动态连接工具使这相当容易,这种共享代码让系统更加高效,易于维护。VxWorks系统主要采用如下的几种可重入技术:(1)动态堆栈变量许多子函数只是纯代码,除了动态堆栈变量外没有其他数据。调用程序的参数作为子函数的数据。这种子函数是完全可重入的,多个任务同时使用这种子函数,不会互相影响,因为它们各有自己的堆栈空间。(2)受保护的全局和静态变量一些函数库包含公有数据,多个任务的同时调用很可能会导致