您当前的位置:五五电子网电子知识单片机-工控设备嵌入式系统-技术微型抢占式多任务实时内核设计 正文
微型抢占式多任务实时内核设计

微型抢占式多任务实时内核设计

点击数:7936 次   录入时间:03-04 11:39:27   整理:http://www.55dianzi.com   嵌入式系统-技术

    2.3 任务切换

??与任务创建一样,任务切换代码与硬件相关。在PC机上,代码和步骤如下:

 void interrupt os_Schedule( )  …………(1)

{

if( os_nLayers )return;

os_nLayers++;   …………(2)

_DX = (int)os_pCurTCB;

/*os_pCurTCB指向当前任务的控制块*/

  *(int*)(_DX+4) = _SP;

  *(int*)(_DX+6) = _SS; …………(3)

  os_GetReadyTask( ); …………(4)

  _DX = (int)os_pCurTCB;

  _SP = *(int*)(_DX+4);

  _DX = *(int*)(_DX+6);

  _SS = _DX;   …………(5)

  os_nLayers--;?   …………(6)

  UNLOCK_INT( );

    }   …………(7)

    (1)利用C语言interrupt关键字使各寄存器入栈保护。(2)锁定调度器,不允许重调度。(3)将当前任务的栈顶地址(由堆栈段寄存器SS和栈指针寄存器SP组成)保存在os_pCurTCB->sp中(PC机下,TCB中的sp定义为远指针类型)。(4)选出优先级最高的就绪任务(方法类似于μC/OS),并将os_pCurTCB指向新任务的控制块。(5)栈寄存器指向新任务的栈顶地址。(6)解锁调度器。(7)各寄存器出栈,恢复到上次被中断时的情形。

3 消息与信号

??为很好地支持事件驱动编程,MicroStar借鉴了Windows的“基于消息,事件驱动”观念,并加以扩展。在MicroStar中,事件不仅可以触发消息、信号,而且由事件触发的消息或信号是有优先级的,这是因为不同事件对处理的实时性要求是不同的。内核正是根据消息、信号的优先级来动态调整任务的动态优先级的。

    3.1 消 息

??消息是一种很友好的通信方式。考虑中低档单片机的内存容量和需求,将消息简化为一个0~31的值。采用固定位图存储格式,将这32个值映射到任务控制块的msg域,这大大减小了存储空间。可将msg域看作一个32位的二进制变量,第i位为1,表示有值为i的消息,因此消息的存取只需通过简单的“与”、“或”运算。消息的优先级依值而定,值越大,优先级越低。在系统范围内,消息优先级又分为两级:紧急级(值0~15)与普通级(值16~31)。当有紧急消息发送给任务时,内核会提升任务的动态优先级,从而提高消息处理的实时性。当任务无紧急消息要处理时,内核就降低它的动态优先级。发送消息的核心代码如下:

/*const uint_16 os_maskTable[16] ={ 0x8000,0x4000, .....,0x0008,0x0004,0x0002,0x0001 */

if( msg&0xF0 ) { /*普通级消息*/

PTCB->msg[1] |= os_maskTable[msg&0x0F];

/*普通级消息存在msg[1]中*/

os_rdyState |= os_maskTable[pTCB->priority];

}

else { /*紧急级消息*/

pTCB->msg[0] |= os_maskTable[msg]; ;

/*紧急级消息存在msg[0]中*/

os_rdyhState |= os_maskTable[pTCB->priority];

/*提升动态优先级*/

}

??与先进先出(FIFO)方式的消息队列不同,内核总是取出优先级最高的消息来交给任务处理。消息接收函数os_GetMessage设计思路如下:如果消息接收区中无紧急消息,则降低任务的动态优先级;如果消息接收区中有消息,则取出优先级最高的消息;如果没有消息,则将任务转为等待态。考虑有时候不希望任务进入等待态,MicroStar还提供了非阻塞的os_PeekMessage消息接收函数。

    3.2 信 号

??在嵌入式系统编程中,常利用标志位来实现前后台程序或不同的任务间的通信。MicroStar也提供了类似的任务间的通信方式——信号(signal)。它避免了用户程序因不断查询标志位而带来的时间浪费,而且支持信号间的“与”、“或”运算。通俗来说,信号就是标志位,用来标识某个事件的发生。同消息一样,信号也有紧急级与普通级之分。与消息不同的是,信号完全由用户程序创建和维护,内核只是帮助用户程序等待信号,以避免低效率的标志位查询。使用起来不如消息直观,但执行效率较高。实现起来非常简单,请参见源码。

图1

4 定时器

??定时器在嵌入式系统有着大量的用途,如LED的定时刷新、串口通信中的超时检查。对定时器的需求分为两类,一种是周期性重复定时,比如每隔10ms去刷新LED;另一种是仅需定时一次的一次性定时。定时时长以系统时钟节拍(tick,又译作滴达)作为单位。两次系统定时中断之间的时间间隔为一个节拍。定时器结构体如下:

typedef struct{

uint_16 elapse; /*定时时长的余值*/

uint_16 backTime; /*定时时长的备份值*/

MSG timerId; /*定时器ID号*/

uchar taskId; /*拥有该定时器的任务的ID*/

TIMERPROC lpTimerFunc; /*定时调用的函数指针*/

}TIMER,*PTIMER;

TIMER os_timers[USER_TIMER_NUM]; /*最多为16个*/

??周期性定时和一次性定时是通过timerId来区分的。如果timeId为64,为一次性定时;如果timerId不大于32,则为周期性定时。用os_timers数组记录定时器信息,用16位的os_timerState表示定时器的状态。如果os_timerState的二进制数的第N位为1,则表示os_timers[N]空闲可用。

??对周期性定时器,每隔定时时长的时间,内核就调用的lpTimerFunc指向的函数,并且将timerId以消息的方式发送给任务,对任务的动态优先级的影响与普通消息一样。因此,要想取得实时性较好的定时器,只需将timerId设在0~15之间。与一次性定时相关的是睡眠函数和限时等待同步对象的函数。任务使用这两个函数而进入休眠态后,在定时时间到时,内核将其恢复为就绪态,并自动释放定时器资源。系统定时处理的核心代码如下:

if( !(--pTimer->elapse) ){ /*elapse减为零表示时间到*/

if( pTimer->lpTimerFunc)(*pTimer->lpTimerFunc)(pTimer->

taskId,pTimer->timerId);

switch( pTimer->timerId&0xF0 ){

case SLEEP_ID: /*一次性定时*/

os_slpState |= taskMask; /*结束休眠态*/

os_timerState |= timerMask; /*释放定时器*/

break;

case 0x00: /*发送紧急级定时器消息*/

pTCB->msg[0] |= os_maskTable[pTimer->timerId];

os_rdyhState |= os_maskTable[pTCB->priority ];;

break;

case 0x10: : /*发送普通级定时器消息*/

pTCB->msg[1] |= os_maskTable[pTimer->timerId&0x0f];

os_rdyState |= os_maskTable[pTCB->priority ];;

}

}

5 同 步

??抢占式多任务下,低优先级的任务可以被高优先级任务打断执行。以常规方式访问共享变量或资源时,会出现奇怪的结果。比如,一个任务调用printf(“12345”)试图在输出设备上输出“12345”,但执行中被高优先级任务打断;而高优先级任务也调用printf(“67890”)试图输出“67890”,最终的输出结果可能是“1267890345”之类。这就是多任务环境下的任务同步问题。 

??同步方式有两种,一种为用户同步方式,不需要与内核打交道,具有速度快的优点,但只适合保护执行时间短的代码;另一种是内核同步方式,需要通过内核来实现,速度相对较慢,但可保护执行时间长的代码。

    5.1 用户同步方式

??用户方式下的同步是通过关键代码段(critical section)保护来实现。关键代码段是指这样一小段代码,它执行时必须独占对某些共享资源的访问权,不允行被其它试图访问该资源的代码打断。最简单的是得用关/开中断来实现,优点是速度极快,缺点是带来中断延迟,只适合执行时间极短的代码段。另一简单的方案是通过加锁/解锁调度器来实现,即在关键代码段执行期间禁止内核进行任务切换。采用这种方法,不会带来中断延迟,但带来了调度延迟。在MicroStar中,对os_nLayers加1即可锁定调度器,减1即可解锁。但直接利用解锁调度器来离开关键代码段并不合适。如果在关键代码段执行中,发生了中断,使更高优先级任务就绪。但由于调度器被锁定,中断程序退出时不能进行任务切换以使高优先级任务执行。因此我们希望,最好一旦调度器解锁,马上就切换到高优先级任务。为此,专门用变量os_flag的最低位作为标志位,中断程序中调用任何可以使任务就绪的系统函数都会影响到该标志位,如os_PostMessage、os_SetEvent,os_Notity。退出关键代码段时以此来判断是否需要进行任务调度。离开临界代码段时的代码如下:

上一页  [1] [2] [3]  下一页


本文关键字:暂无联系方式嵌入式系统-技术单片机-工控设备 - 嵌入式系统-技术

《微型抢占式多任务实时内核设计》相关文章>>>