01. 分配任务优先级与RMS调度
1.1. RMS调度算法
RMS(单调速率调度算法)是一种静态优先级调度算法,是经典的周期性任务调度算法。RMS的基本思路是任务的优先级与它的周期表现为单调函数的关系,任务的周期越短,优先级越高;任务的周期越长,优先级越低。如果存在一种基于静态优先级的调度顺序,使得每个任务都能在其期限时间内完成。
基于RMS 的一个基础理论公式
1.2 RMS调度实例
任务 | 执行时间 | 任务周期 |
---|---|---|
TASK1 | 1 | 10 |
TASK2 | 1 | 5 |
TASK3 | 2 | 10 |
1)系统CPU使用率: 1/10+1/5+2/10 = 0.5
2) 3个任务时,RMS可实时调度率为:
实例结论: CPU占用率0.5 小于可实时调度率0.77, 所以这3个任务理论上是可以实时调度的。
1.3 基于RMS算法的任务优先级分配原则:
任务频率越高,优先级越高;任务执行时间越长,优先级越低
1.4. 最大任务优先级配置
最大任务优先级,影响查询最高优先级就绪任务时间, 任务最大优先级最好配置为32
OS_PRIO OS_PrioGetHighest (void) //获取最高优级就绪任务
{
CPU_DATA *p_tbl;
OS_PRIO prio;
prio = (OS_PRIO)0;
p_tbl = &OSPrioTbl[0];
while (*p_tbl == (CPU_DATA)0) { /* Search the bitmap table for the highest priority */
prio += DEF_INT_CPU_NBR_BITS; /* Compute the step of each CPU_DATA entry */
p_tbl++;
}
prio += (OS_PRIO)CPU_CntLeadZeros(*p_tbl); /* Find the position of the first bit set at the entry */
return (prio);
}
02. 任务堆栈管理
2.1 如何确定任务堆栈的大小
1) 手动计算任务堆栈大小
任务内所有函数调用嵌套所需的所有内存,每个函数调用一个返回地址的指针,每个函数调用中传递的所有参数,以及每个函数分配的局部变量,得到一个大约数后再乘以1.5~2的安全系数。
2) 通过uc/probe 监控运行时任务堆栈占用情况,根据实际情况调整。
3)任务函数设计的注意事项
a. 任务内避免函数的多层嵌套调用 b. 任务内避免递归函数 c. 避免分配大的局部变量
2.2 检测任务堆栈溢出
1)UCOS-III 监测任务运行时的最大堆栈: OS_StatTask()->OSTaskStkChk
2) 堆栈溢出检测: OSRedzoneHitHook
03. 任务状态切换图
04. 任务抢占式调度
任务抢占式调度过程分析:
(1) 低优先级任务正在执行,发生中断。
(2) 如果开启了中断,则CPU跳转到中断服务ISR中。
(3) ISR处理数据,向更高优先级的任务发出信号或消息,高优先级任务就绪
(4) 当 ISR 完成后, 触发任务调度
(6) 执行较高优先级的任务
(8) 较高优先级的任务执行完成后, 进入阻塞状态并触发任务调度
(10)切换回原始任务(被中断的任务)
(11) 被中断的任务在它被中断的地方恢复执行。
05. 任务时间片轮调度
时间片轮调度: 当两个或多个任务具有相同的优先级时,以设定好的时间片循环调度
时间片轮调度的时序图
(1) 任务#3正在执行,在此期间发生TICK中断,但任务 #3 的时间量尚未到期。
(2) 在第4个TICK答中断时,任务#3的时间量到期
(3) µC/OS-III 恢复任务#1,因为它是任务列表中优先级为“X”的下一个准备运行的任务。
(4) 任务#1 执行直到它的时间量到期
(7) 这里任务#3 执行,调用了OSSchedRoundRobinYield()函数放弃它的时间量,任务 #1开始运行
(8) 任务#1 执行直到它的时间量到期
06. 系统空闲任务
系统空闲任务OS_IdleTask()是 µC/OS-III 创建的第一个任务。空闲任务的优先级始终设置为OS_CFG_PRIO_MAX-1, 空闲任务是一个“真正的”无限循环。空闲任务主要用于测量CPU的使用率。
void OS_IdleTask (void *p_arg)
{
while (DEF_ON) { (1)
CPU_CRITICAL_ENTER();
OSIdleTaskCtr++; (2)
OSStatTaskCtr++;
CPU_CRITICAL_EXIT();
OSIdleTaskHook(); (3)
}
}
07. 系统统计任务
统计任务用于统计cpu使用率、每个任务cpu使用率、堆栈使用情况等运行时系统信息。
cpu占用率的统计
1)获取只有空闲任务运行(CPU空载)时,OSStatTaskCtrMax的值
OSStatTaskCPUUsageInit
OSStatReset
2)OSStatTaskCPUUsage/100,即cpu使用率
void main (void)
{
OS_ERR err;
:
OSInit(&err);
if (err != OS_ERR_NONE) {
/* Something wasn't configured properly, µC/OS-III not properly initialized */
/* ... 'err' will tell you the cause (see os.h) */
}
/* (3) Create ONE task (we'll call it AppTaskStart() for sake of discussion) */
:
OSStart(&err);
}
void AppTaskStart (void *p_arg)
{
OS_ERR err;
:
/* (5) Initialize the tick interrupt */
#if OS_CFG_STAT_TASK_EN == DEF_ENABLED
OSStatTaskCPUUsageInit(&err);
OSStatReset(&err)
#endif
:
/* Create other tasks */
while (DEF_ON) {
/* AppTaskStart() body */
}
}
08. 系统软定时器任务
软定时器与时钟滴答使用相同的中断源,但周期比时钟滴答长
09. 任务控制块详解
struct os_tcb {
CPU_STK *StkPtr; //指向任务当前栈顶的指针
void *ExtPtr; //指向用户定义数据区域的指针
CPU_CHAR *NamePtr; //任务创建时定义的任务名称
CPU_STK *StkLimitPtr;//堆栈溢出检测门限
OS_TCB *NextPtr;
OS_TCB *PrevPtr; //NextPtr, PrevPtr就绪/阻塞队列中的双向链表指针
OS_TCB *TickNextPtr;
OS_TCB *TickPrevPtr;; //TickNextPtr, TickPrevPtr 延时到期/挂起超时队列中的双向链表指针
OS_TICK_LIST *TickListPtr; //指向延时到期队列/挂起超时队列
CPU_STK *StkBasePtr; //指向任务堆栈的起始地址
OS_TLS TLS_Tbl[OS_CFG_TLS_TBL_SIZE] //编译器用于线程安全的数组
OS_TASK_PTR TaskEntryAddr; //任务入口地址
void *TaskEntryArg; //任务启动时传递的参数
OS_TCB *PendNextPtr;
OS_TCB *PendPrevPtr; //PendNextPtr,PendPrevPtr 等待列表中内核对象的双向链表指针
OS_PEND_OBJ *PendObjPtr; //指向挂起的内核对象
OS_STATE PendOn; //任务等待的内核对象
OS_STATUS PendStatus; //任务挂起的结果OS_STATUS_PEND_OK,OS_STATUS_PEND_ABORT,OS_STATUS_PEND_DEL,OS_STATUS_PEND_TIMEOUT
OS_STATE TaskState; //任务状态
OS_PRIO Prio; //任务当前优先级
OS_PRIO BasePrio; //任务创建时的优先级
OS_MUTEX *MutexGrpHeadPtr; //指向任务拥有的互斥锁列表
CPU_STK_SIZE StkSize; //任务堆栈大小
OS_OPT Opt; //任务创建时传递的opt
CPU_TS TS; //任务唤醒时的时间戳
CPU_INT08U SemID; //第三方工具TraceAlyzer使用
OS_SEM_CTR SemCtr; //任务内部信号量计数值
OS_TICK TickRemain; //从延迟或超时列表中删除任务之前剩余的tick
OS_TICK TickCtrPrev; //用于OSTimeDly周期模式时的延时时间重载
OS_TICK TimeQuanta;
OS_TICK TimeQuantaCtr; // TimeQuanta,TimeQuantaCtr用于时间切片片轮调度
void *MsgPtr; // 接收到的消息
OS_MSG_SIZE MsgSize; // 接收到的消息大小
OS_MSG_Q MsgQ; // 任务消息队列
CPU_TS MsgQPendTime; // 最近一条消息传输消耗的时间
CPU_TS MsgQPendTimeMax; //消息传输消耗的最大时间
OS_REG RegTbl[OS_TASK_REG_TBL_SIZE];//特定寄存器
OS_FLAGS FlagsPend; //任务被哪个事件挂起
OS_FLAGS FlagsRdy; //任务被哪个事件唤醒
OS_OPT FlagsOpt; //挂起任务的事件属于哪种类型
OS_NESTING_CTR SuspendCtr; //任务被OSTaskSuspend()挂起的次数
OS_CPU_USAGE CPUUsage; //任务当前的cpu占用率
OS_CPU_USAGE CPUUsageMax; //任务最大的cpu占用率
OS_CTX_SW_CTR CtxSwCtr; //用于调试器监视任务的执行频率
CPU_TS CyclesDelta; //用于调试器监视任务最近一次执行的时间
CPU_TS CyclesStart; //用于调试器监视任务开始执行的时间
OS_CYCLES CyclesTotal; //用于调试器监视任务运行的总时长
OS_CYCLES CyclesTotalPrev;//Snapshot of previous # of cycles
CPU_TS SemPendTime; //任务最近一次接收信号量消耗的时间
CPU_TS SemPendTimeMax; //任务接收信号量消耗的最长时间
CPU_STK_SIZE StkUsed; //已使用堆栈的大小
CPU_STK_SIZE StkFree; //剩余堆栈的大小
CPU_TS IntDisTimeMax; //任务关闭中断的最长时间
CPU SchedLockTimeMax;//任务关闭任务调度的最长时间
OS_TCB *DbgPrevPtr;
OS_TCB *DbgNextPtr;
CPU_CHAR *DbgNamePtr;
CPU_INT08U TaskID; //用于TraceAlyzer 等第三方工具
};
10. 系统调度相关API
10.1 任务相关API
10.2 任务阻塞相关API
10.3 任务唤醒相关API