一、任务管理机制
1、 任务管理用来实现对任务状态的直接控制和访问。
2、内核的任务管理是通过系统调用来体现,主要包括任务创建、任务删除、任务挂起、任务唤醒、设置任务属性等内容。
3、创建任务的过程即为分配任务控制块的过程。
a、在创建任务时,通常需要确定任务的名字和任务的优先级等内容,确立任务所能使用的堆栈区域。
b、任务创建成功后,通常会为用户返回一个标识该任务的,ID以实现对任务的引用管理。
4、删除任务:把任务从系统中去掉,释放对应的任务控制块。
5、挂起/唤醒任务:把任务变为等待状态,可通过唤醒任务操作把任务转换为就绪状态。
6、设置任务属性可以用来设置任务的抢占、时间片等特性,以确定是否允许任务在执行过程中被抢占或是对同优先级任务采用时间片轮转方式运行等。
7、改变任务优先级用来根据需要改变任务的当前优先级。
8、获取任务信息获得任务的当前优先级、任务的属性、任务的名字、任务的上下文、任务的状态等内容,便于用户进行决策。
二、任务建立
想让μC/OS-Ⅱ管理用户的任务,用户必须要先建立任务。用户可以通过传递任务地址和其它参数到以下两个函数之一来建立任务:OSTaskCreate() 或 OSTaskCreateExt()。OSTaskCreate()与μC/OS是向下兼容的,OSTaskCreateExt()是OSTaskCreate()的扩展版本,提供了一些附加的功能。用两个函数中的任何一个都可以建立任务。任务可以在多任务调度开始前建立,也可以在其它任务的执行过程中被建立。在开始多任务调度(即调用OSStart())前,用户必须建立至少一个任务。任务不能由中断服务程序(ISR)来建立。
OSTaskCreate()的代码如程序清单如下。从中可以知道,OSTaskCreate()需要四个参数:task是任务代码的指针,pdata是当任务开始执行时传递给任务的参数的指针,ptos是分配给任务的堆栈的栈顶指针(参看4.02,任务堆栈),prio是分配给任务的优先级。
INT8U OSTaskCreate (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT8U prio)
{
void *psp;
INT8U err;
if (prio > OS_LOWEST_PRIO) { (1)
return (OS_PRIO_INVALID);
}
OS_ENTER_CRITICAL();
if (OSTCBPrioTbl[prio] == (OS_TCB *)0) { (2)
OSTCBPrioTbl[prio] = (OS_TCB *)1; (3)
OS_EXIT_CRITICAL(); (4)
psp = (void *)OSTaskStkInit(task, pdata, ptos, 0); (5)
err = OSTCBInit(prio, psp, (void *)0, 0, 0, (void *)0, 0); (6)
if (err == OS_NO_ERR) { (7)
OS_ENTER_CRITICAL();
OSTaskCtr++; (8)
OSTaskCreateHook(OSTCBPrioTbl[prio]); (9)
OS_EXIT_CRITICAL();
if (OSRunning) { (10)
OSSched(); (11)
}
} else {
OS_ENTER_CRITICAL();
OSTCBPrioTbl[prio] = (OS_TCB *)0; (12)
OS_EXIT_CRITICAL();
}
return (err);
} else {
OS_EXIT_CRITICAL();
return (OS_PRIO_EXIST);
}
}
三、任务堆栈
每个任务都有自己的堆栈空间。堆栈必须声明为OS_STK类型,并且由连续的内存空间组成。用户可以静态分配堆栈空间(在编译的时候分配)也可以动态地分配堆栈空间(在运行的时候分配)。静态堆栈声明如程序清单 L4.4和4.5所示,这两种声明应放置在函数的外面。
用户可以用C编译器提供的malloc()函数来动态地分配堆栈空间,如程序清单 L4.6所示。在动态分配中,用户要时刻注意内存碎片问题。特别是当用户反复地建立和删除任务时,内存堆中可能会出现大量的内存碎片,导致没有足够大的一块连续内存区域可用作任务堆栈,这时malloc()便无法成功地为任务分配堆栈空间。
OS_STK *pstk;
pstk = (OS_STK *)malloc(stack_size);
if (pstk != (OS_STK *)0) {
Create the task;
}
四、删除任务
INT8U OSTaskDel (INT8U prio)
{
OS_TCB *ptcb;
OS_EVENT *pevent;
if (prio == OS_IDLE_PRIO) { (1)
return (OS_TASK_DEL_IDLE);
}
if (prio >= OS_LOWEST_PRIO && prio != OS_PRIO_SELF) { (2)
return (OS_PRIO_INVALID);
}
OS_ENTER_CRITICAL();
if (OSIntNesting > 0) { (3)
OS_EXIT_CRITICAL();
return (OS_TASK_DEL_ISR);
}
if (prio == OS_PRIO_SELF) { (4)
Prio = OSTCBCur->OSTCBPrio;
}
if ((ptcb = OSTCBPrioTbl[prio]) != (OS_TCB *)0) { (5)
if ((OSRdyTbl[ptcb->OSTCBY] &= ~ptcb->OSTCBBitX) == 0) { (6)
OSRdyGrp &= ~ptcb->OSTCBBitY;
}
if ((pevent = ptcb->OSTCBEventPtr) != (OS_EVENT *)0) { (7)
if ((pevent->OSEventTbl[ptcb->OSTCBY] &= ~ptcb->OSTCBBitX) == 0) {
pevent->OSEventGrp &= ~ptcb->OSTCBBitY;
}
}
Ptcb->OSTCBDly = 0; (8)
Ptcb->OSTCBStat = OS_STAT_RDY; (9)
OSLockNesting++; (10)
OS_EXIT_CRITICAL(); (11)
OSDummy(); (12)
五、挂起任务
INT8U OSTaskSuspend (INT8U prio)
{
BOOLEAN self;
OS_TCB *ptcb;
if (prio == OS_IDLE_PRIO) { (1)
return (OS_TASK_SUSPEND_IDLE);
}
if (prio >= OS_LOWEST_PRIO && prio != OS_PRIO_SELF) { (2)
return (OS_PRIO_INVALID);
}
OS_ENTER_CRITICAL();
if (prio == OS_PRIO_SELF) { (3)
prio = OSTCBCur->OSTCBPrio;
self = TRUE;
} else if (prio == OSTCBCur->OSTCBPrio) { (4)
self = TRUE;
} else {
self = FALSE;
}
if ((ptcb = OSTCBPrioTbl[prio]) == (OS_TCB *)0) { (5)
OS_EXIT_CRITICAL();
return (OS_TASK_SUSPEND_PRIO);
} else {
if ((OSRdyTbl[ptcb->OSTCBY] &= ~ptcb->OSTCBBitX) == 0) { (6)
OSRdyGrp &= ~ptcb->OSTCBBitY;
}
ptcb->OSTCBStat |= OS_STAT_SUSPEND; (7)
OS_EXIT_CRITICAL();
if (self == TRUE) { (8)
OSSched();
}
return (OS_NO_ERR);
}
}
六、改变任务优先级
INT8U OSTaskChangePrio (INT8U oldprio, INT8U newprio)
{
OS_TCB *ptcb;
OS_EVENT *pevent;
INT8U x;
INT8U y;
INT8U bitx;
INT8U bity;
if ((oldprio >= OS_LOWEST_PRIO && oldprio != OS_PRIO_SELF) || (1)
newprio >= OS_LOWEST_PRIO) {
return (OS_PRIO_INVALID);
}
OS_ENTER_CRITICAL();
if (OSTCBPrioTbl[newprio] != (OS_TCB *)0) { (2)
OS_EXIT_CRITICAL();
return (OS_PRIO_EXIST);
} else {
OSTCBPrioTbl[newprio] = (OS_TCB *)1; (3)
OS_EXIT_CRITICAL();
y = newprio >> 3;
bity = OSMapTbl[y];
x = newprio & 0x07;
bitx = OSMapTbl[x];
OS_ENTER_CRITICAL();
if (oldprio == OS_PRIO_SELF) { (5)
oldprio = OSTCBCur->OSTCBPrio;
}
if ((ptcb = OSTCBPrioTbl[oldprio]) != (OS_TCB *)0) { (6)
OSTCBPrioTbl[oldprio] = (OS_TCB *)0; (7)
if (OSRdyTbl[ptcb->OSTCBY] & ptcb->OSTCBBitX) { (8)
if ((OSRdyTbl[ptcb->OSTCBY] &= ~ptcb->OSTCBBitX) == 0) {(9)
OSRdyGrp &= ~ptcb->OSTCBBitY;
}
OSRdyGrp |= bity; (10)
OSRdyTbl[y] |= bitx;
} else {
if ((pevent = ptcb->OSTCBEventPtr) != (OS_EVENT *)0) { (11)
if ((pevent->OSEventTbl[ptcb->OSTCBY] &= ~ptcb->OSTCBBitX) == 0) {
pevent->OSEventGrp &= ~ptcb->OSTCBBitY;
}
pevent->OSEventGrp |= bity; (12)
pevent->OSEventTbl[y] |= bitx;
}
}
OSTCBPrioTbl[newprio] = ptcb; (13)
ptcb->OSTCBPrio = newprio; (14)
ptcb->OSTCBY = y; (15)
ptcb->OSTCBX = x;
ptcb->OSTCBBitY = bity;
ptcb->OSTCBBitX = bitx;
OS_EXIT_CRITICAL();
OSSched(); (16)
return (OS_NO_ERR);
} else {
OSTCBPrioTbl[newprio] = (OS_TCB *)0; (17)
OS_EXIT_CRITICAL();
return (OS_PRIO_ERR);
}
}
}
一、1、 调度用来确定多任务环境下任务执行的顺序和在获得CPU资源后能够执行的时间长度。
2、 操作系统通过一个调度程序来实现调度功能。
调度程序以函数的形式存在,用来实现操作系统的调度算法。调度程序本身并不是一个任务,是一个函数调用,可在内核的各个部分进行调用。
3、调用调度程序的具体位置又被称为是一个调度点(scheduling point),调度点通常处于以下位置:中断服务程序的结束位置;任务因等待资源而处于等待状态;任务处于就绪状态时等。
二、调度算法
从理论上来说,最优调度只有在能够完全获知所有任务在处理、同步和通信方面的需求,以及硬件的处理和时间特性的基础上才能实现。实际的应用很难实现,特别是需要获知的信息处于动态变化的情况下。即使在这些需要的信息都是可以预见的情况下,常用的调度问题仍然是一个NP难题。调度的复杂性将随调度需要考虑的任务和约束特性的数量呈现出指数增长。调度算法不能很好地适应系统负载和硬件资源不断增长的系统。当然,这并不意味着调度算法不能解决只有少量、定义好的任务的应用的需求。
嵌入式实时操作系统兼有嵌入式和实时性的特点。作为一种嵌入式操作系统,它具有嵌入式软件共有的可裁剪、低资源、低功耗等特点;作为实时操作系统除了要满足应用的功能需求以外,更重要的是还要满足应用提出的实时性要求。实时操作系统所遵循的最重要的# #设计原则是:采用各种算法和策略始终保证系统行为的可预测性。实时操作系统的首要任务是调动一切可利用的资源完成实时控制任务。如何使任务集内各任务满足各自的时限,使系统得以正常、高效率工作的任务调度算法一直是实时系统领域内研究的焦点。根据其应用领域及追求精简、高设角度的不同,任务调度算法从简单的合理安排任务循环,发展到基于优先级的速率单调调(RMs)、最早时限优先(EDF)等算法。任务调度算法的好坏以及执行效率直接关系到嵌入式内核的应用范围及实时性程度。
1、各种实时操作系统的实时调度算法可以分为如下三种类别
基于优先级的调度算法(Priority-driven scheduling-PD)、基于CPU使用比例的共享式的调度算法(Share-drivescheduling-SD)、以及基于时间的进程调度算法(Time—driven schedulinq-TD),下面对第一种调度算法进行重点介绍。
基于优先级的调度算法给每个进程分配一个优先级,在每次进程调度时,调度器总是调度那个具有最高优先级的任务来执行。根据不同的优先级分配方法,基于优先级的调度算
法可以分为如下两种类型
1.1 静态调度
静态调度是在系统开始运行前进行调度的,严格的静态调度在系统运行时无法对任务进行重新调度。静态调度的目标是把任务分配到各个处理机,并对每一处理机给出所要运行任务的静态运行顺序。静态调度算法实现简单,调度的额外开销小,在系统超载时可预测性好。但也具有很大的局限性,例如资源利用率低、受系统支持的优先级个数限制以及灵活性和自适应性差等。
1.2 动态调度
在嵌入式实时系统中,动态调度依赖于任务的优先级。优先级可以静态分配或者依据不同的特征参数,如截止时问、空闲时间或关键性(即任务的重要程度)等进行动态分配。动态
调度可以是抢占式的或非抢占式的。当检查到一事件时,动态抢占式算法立即决定是运行与此事件相关的任务,或继续执行当前的任务;对于动态非抢占式算法,它仅仅知道有另一
个任务可以运行,在当前任务结束后,它才在就绪的任务中选择一个来运行。
三、调度算法的选择 嵌入式实时系统中资源是非常有限的,所以开销要尽可能小。开销主要包括运行开销和调度开销。运行开销与队列分析和从调度队列中增加、删除任务相关。每个任务在一个调度周期内至少被阻塞和唤醒一次,所以任务调度器在一个周期内不得不对一个任务进行两次选择。静态算法根据任务的执行频率设置优先级,有较小的运行开销,但执行频率最高的任务不一定是最重要的。EDF算法中则是对整个任务列表的调度开销进行全面比较,选择最高优先级任务进行调度,有较小的调度开销,但对多个任务具有同一优先级的情况考虑不足。基于优先级的调度算法在实时进程调度中使用很广泛,静态优先级调度算法根据应用的属性来分配优先级,其可控性较强,而动态优先级调度算法在资源分配和调度时具有更大的灵活性。如果结合这两种算法的优点,扬长避短,就能够对实时任务进行更合理、更高效的任务调度。利用最著名的动态优先级调度算法一EDF算法的高CPU利用率、可调度较大的任务集的特点,结合静态优先级调度算法的可控性就形成了一种新的调度算法-NEDF、调度算法(New Earliest Dead—line First)。
NEDF算法概述
NEDF算法以任务的截止期限作为任务调度的首要指标,但不是唯一的指标。当两任务的截止期限在一定的IM值范围内时,根据任务的优先级来决定要运行的任务,这时以任务的静态优先级来选择任务,一定程度上增强了算法的可控性。确定任务的静态优先级,主要依据有以下几个。
(1)执行时间
以执行时间为依据,执行时间越短,静态优先级越高。
(2)任务周期
以任务周期为依据,任务周期越短,静态优先级越高。
(3)任务的CPU利用率
任务的CPU利用率为任务执行时间与任务周期的比值(生)。仟各的CPU利基于Linux的实时操作系统研究用率越高,静态优先级越高。(4)任务紧急程度根据任务的紧急程度,人为安排任务的优先级。任务越紧急,静态优先级越高。
算法说明
先假定任务的优先级均不相同,则在某个调度时刻t,NEDF算法先查找距截止期限最近的任务。这时,可能有多个任务的截止期限相等或较为接近。如果截止期限相等,则选择
高优先级的任务运行。如果截止期限均不相等,且最小截止期限比次小截止期限小许多,则选择最小截止期限的任务运行。若最小截止期限与次小截止期限的差值在一定的IM值范围
内,则选择高优先级的任务运行。截止期限IM 值的设定应保证最高优先级任务能够如期完成,一般可取最小相对截止期限的值,以确保在最小相对截止期限的周期范围内,最高优先
级任务能够优先运行。