|
1, 基本操作系统现代时实操作系统主要的补充了多任务处理和任务间通讯两个概念。多任务环境意味着允许在一个时实应用程序中构架一组独立的任务。每一个都有自己独立的执行路线和自己的系统资源。任务间通讯的机制(facility)则允许任务间的同步和通讯,以调整系统的行为。VxWorks中,任务间通讯的机制包括从快速信号量到消息队列,管道,网络传输套接口。
另一个时实系统的关键机制是硬件中断处理。因为中断常常是构成系统外部事件的机制。
为了达到中断的快速响应。中断处理程序(ISR)使用一种特殊的上下文,不同与任务的上下文。以下将讨论多任务内核,任务机制,任务间通讯,和中断处理机制。这些是VxWork s运行时环境的核心。
Wind特征和POSIX特征:POSIX的时实扩展标准(1003.1b)定义了一组特殊的内核机制。为了提高应用程序的集成性,Wind,Vxworks的内核,同时包括了POSIX接口和VxWorks的特殊接口。
在本文中“Wind”特指VxWorks的wind内核机制所有的特性。
任务:我们经常需要把应用程序组织成独立的,相互协作的一些程序。每一个单独执行的程序都是一个任务。在Vxworks中,任务能直接共享所有的系统资源,当然各个独立的线程控制所需要的各自的上下文是必需保留的。
多任务:多任务处理提供了进行对应用程序控制 和对多样的,离散的真实世界事件的反应 的基本机制。WXWORKS的时实内核,提供了基本的多任务环境。多任务机制从表面上看起来是创建了许多同时运行的线程,但实际上是内核在按照一定的数学法则对它们的执行进行调度。
每一个显然是独立的程序都叫做一个任务。每个任务都有自己的上下文。(所谓上下文是指:有系统内核运行调度的任务可能会在任何时刻访问的CPU环境和系统资源。)在一次上下文切换中,一个任务的上下文将保存在任务控制块中(TCB)。一个任务的上下文包括以下内容:
线程执行情况,即任务程序记数器。
CPU寄存器和浮点寄存器。
动态数据堆栈和函数调用。
标准输入,输出和错误的I/O分配。
延时定时器。
时间片定时器。
内核控制结构。
信号句柄。
调试和运行监视值。
在VxWorks中,一个不属于上下文的重要资源是内存和地址空间:所有的代码都在一个共同的单一地址空间中执行。要让每一个任务都有自己的地址空间就需要进行虚拟—物理内存映射,但这需要选择使用VxVMI模块以支持虚存功能。
任务状态切换:内核维护系统中所有任务的当前状态。一个任务的的状态变化是应用程序进行内核函数调用的结果。但任务创建时,它处于挂起状态。只有激活任务,才能进入准备状态。激活阶段是非常短暂的。因此最好让应用程序在早期预先创建好任务,以便在需要时及时启动。另外一种创建任务的方法是使用spawning 原语,它可以同时创建和激活任务。任务可以在任何状态下删除。
Wind微内核的状态迁移表如下图所示:
Figure 2-1
Wind内核任务调度:多任务处理需要一个调度法则对CPU准备运行的任务进行分配。对于wind内核来说,基于优先级的强占调度方式是系统的默认工作方式。当然,也可以根据应用程序的需要选择时间片轮转的调度方式。
优先抢占调度:在一个基于优先级的抢占式调度系统中,每一个任务都有一个优先级。内核保证把CPU分配给优先级较高的已准备好运行的任务。这种调度模式意味着只要有高优先级的任务处于READY状态,内核将立即存储当前任务的上下文,并切换到高优先级任务的上下文。如下图所示,任务T1被高优先级的T2任务抢占,T2又被T3抢占。T3运行完后,T2继续运行,T2运行完后,T1才能继续运行。
Wind内核总共有256个优先级,数目从0——255.优先级0是最高的255最低。(与PSOS正好相反)任务在创建是将分配一个优先级。在运行过程中也可以使用taskPrioritySet()函数改变优先级。这种设计可以使应用程序更贴近现实。
轮转调度:(Round-Robin Scheduling)
优先强占调度方式可以扩展为轮转调度方式。轮转调度方式的主要作用是允许相同优先级的准备好的任务在运行时公平地分享CPU.如果没有轮转调度,那么当多个同优先级任务必需共享处理器时,一个任务如果不阻塞的话,就可能会独占CPU,导致其它所有的任务都无法运行。
轮转调度实现在同级任务中公平分配CPU资源的方法一般是时间片轮转。一组任务中的每个任务执行指定的时间间隔或时间片;然后另一个任务执行相同的时间间隔,依次类推。这种调度方法的公平之处在于,只有所有的任务都获得过依次时间间隔运行后,才能有任务获得第二个时间片运行的机会。
系统中使用函数kernelTimeSlice()允许系统实现轮转调度。
更精确地,每个任务有一个运行时间记数器(run-time counter)记录增加的始终TICK数。当一个特定的时间片结束时。计数器将清空,并且将任务挂到同级任务的队尾。当一个新任务加入优先级组时,本任务将挂到队尾,并且将运行时间计数器初始化为0.如果一个任务在自己的运行时间间隔中被高优先级的任务抢占,它的运行时间计数器将被保存,直到任务重新运行时再恢复。下图是轮转调度系统的示例:T1,T2,T3是同级任务,T2被高优先级的T4任务抢占,当T4运行完后,T2继续运行至结束。
Wind 内核调度器可以在一个任务中通过taskLock()和taskUnLock()函数,明确地禁止或使能。当一个任务通过调用taskLock()函数禁止内核调度时,这个任务将运行在不被其它任务中断的方式下。
但是,如果此任务明确地阻塞或挂起,调度器将选择一个优先级最高的合格任务执行。不过,当关闭了调度器的任务阻塞解除再次执行时,抢占式调度将再次被关闭。
注意:抢占调度关闭只是禁止任务上下文的切换,但是并不禁止中断处理的发生。
抢占禁止可以用于实现互斥(mutual exclusion)。不过最好让调度禁止的时间尽量短。
任务控制:以下将论述VxWorks与任务有关的系统调用的基本情况。它们存在与VxWorks的库文件task Lib中。这些系统调用包括任务创建,控制,和信息获取。
任务创建:
其它的有关任务的系统调用的函数详见系统手册
任务名和ID号当一个任务产生时,可以使用一个指定的任意长的ASCII字符串作为任务的任务名。VxWo rks系统将返回一个四字节的任务ID号作为任务数据结构的句柄。所有的任务程序获得一个ID作为不同任务的标识。VxWorks系统中约定ID=0表示正在调度的任务。
一个任务的名字不得和任何已经存在的任务名字相冲突。此外,在使用Tornado开发工具进行开发时,最好不要让任务名和任何全局变量名以及全局函数名相冲突。为了避免冲突,VxWorks中约定使用前缀“t”作为运行在目标机上任务名的首字母,而运行在主机上任务名的首字母为“u”。
如果你不想给所有的任务命名,NULL指针也是允许使用的,此时系统分配一个唯一的名字“tN”给你的任务,其中N是一个依次排列的整数。
注:在shell中,任务名由与之相关的任务ID号决定,以简化现有任务的交互工作。
如果要使用浮点运算,则一定要在创建任务时选择VX_FP_TASK.任务删除和删除安全保护:任务可以动态从系统中删除。VxWorks包括的关于任务删除的系统调用如下所示:
警告:在删除任务前,一定要先将任务占用的共享资源释放掉。
Exit()函数是任务程序return返回时默认的处理函数,它也可以被任务程序显式地引用,以在任意点终止任务本身。一个任务还可以使用taskDelete()函数终止另一个任务的执行。
当要删除一个任务的时候,系统并不通知其他任务进行了删除工作。可能会产生一些问题,比如要被删除的任务正处于临界区,或则和临界区相关。这时我们不希望任务被删除。
为此,系统提供两个函数调用保护任务的删除。taskSafe(),taskUnSafe()。taskSafe保护任务不被删除,而taskUnsafe()则退出删除保护。
以下是任务保护的使用方法:
taskSafe ();semTake (semId, WAIT_FOREVER); /* Block until semaphore available */。。 critical region。
semGive (semId); /* Release semaphore */ taskUnsafe ();
在进入临界区前进行任务保护操作,退出临界区后撤消任务保护。
由于任务删除保护和互斥操作经常成对出现,为方便起见,系统专门提供了一种特殊的信号量,可以提供任务保护的选项,在这里不在累述。
任务控制:任务控制的系统调用如下表所示
挂起和启动任务的系统调用,主要是为VxWorks调试工具准备的。它们用于冻结一个任务的状态供检测调试使用。
当任务产生崩溃性错误的时候,可能需要使用任务重起的处理方法。在重起机制中,task Restart()函数将重新使用以前的参数创建一个任务。Tornado shell同样使用这种机制响应任务终止请求。
延时操作提供了一个简单的机制给任务以睡眠一个固定的时间……任务延时主要用于定时循环消息检测。如果不知道CPU的主频,可以使用以下方法延迟固定时间:taskDelay(sysClkRateGet()*时间)
如果使用taskDelay(NO_WAIT);则等待时间为0,但是系统将暂停此任务的运行,而运行其它同优先级的任务。
任务扩展:为了能在系统中实现与任务相关的附加机制,而无需改动内核。Wind内核提供了任务创建,切换和删除的挂钩(hook)。允许在进行任务操作时唤醒附加的程序。任务空制块(TC B)中已经预留了支持任务扩展上下文的空间。支持任务扩展的调度函数如下所示:
用户安装的切换挂钩是在内核上下文中调用的。因此,切换挂钩不能进入所有的VxWorks机制。下面列出可以在切换挂钩函数中调用的VxWorks函数。一般地,所有不使用内核访问系统调用的函数,都可以在挂钩函数中调用。
POSIX 系统调度接口:系统由schedPxLib库提供了POSIX 1003.1b标准调度函数。这组函数可以使你能方便地完成诸如任务优先级设置,取得调度策略,取得所有任务中最大或最小的优先级,循环调度是否有效,取得时间片的长度,等等。为了理解怎样使用函数选择本接口,必需先了解PO SIX与Wind内核在调度方法上的一些小的不同。
POSIX和WIND在调度方法上的不同:
POSIX调度方法是基于进程的,而Wind调度这是基于任务。进程和任务的不同表现在以下几个方面:1, 任务可以直接定位内存,而进程不可以。
2, 进程只能继承父进程的一些特定的属性,而任务操作的环境和父任务的环境完全一样。
任务和进程相似的地方在于都可以单独地被调度。
VxWorks使用抢占优先级的调度。而POSIX标准则使用FIFO先进先出的调度方式。(FIFO即同优先级的调度策略)
POSIX调度算法使用一个进程执行完再执行另一个进程的调度方式。而WIND应用的调度算法是基于一个系统时间带宽基准,无论是时间片轮转的设计或者是优先抢占的设计都是如此。
POSIX优先级的排列顺序和Wind的设计相反。POSIX中,大数字代表高的优先级。在Wind设计中,小数字代表高的优先级,0的优先级最高。
因此,在schedPxLib库中使用的POSIX调度方法的优先级与VxWorks其它模块认可的优先级不匹配。你可以通过设置变量posixPriorityNumbering 为FALSE来禁止掉POSIX形式的优先级设计,这样schedPxLib的优先级数就可以和VxWorks的其它模块通用了。
|
|