11.11 Round-Robin调度
μCOS-III中的时间片调度功能做的很完善,支持全局的时间片设置,也支持每个任务的单独设置。关于时间片调度,咱们在前几期教程也有讲解。
11.11.1 配置参数OSSchedRoundRobinCfg()- /*
- ***********************************************************************************************************
- * 配置ROUND-ROBIN调度参数
- *
- * 函数功能: 调用此函数用更改ROUND-ROBIN调度的参数。
- * 形 参: en DEF_EN:使能ROUND-ROBIN
- * DEF_DIS:禁止ROUND-ROBIN
- * dflt_time_quanta 默认的时间片数,如果是0 那么就表示时间片是默认的OSCfg_TickRate_Hz / 10.
- * p_err 错误代码指针变量
- * OS_ERR_NONE 调用成功
- * 返 回 值: 无
- ***********************************************************************************************************
- */
-
- #if OS_CFG_SCHED_ROUND_ROBIN_EN > 0u
- void OSSchedRoundRobinCfg (CPU_BOOLEAN en,
- OS_TICK dflt_time_quanta,
- OS_ERR *p_err)
- {
- CPU_SR_ALLOC();
-
-
- #ifdef OS_SAFETY_CRITICAL
- if (p_err == (OS_ERR *)0) {
- OS_SAFETY_CRITICAL_EXCEPTION();
- return;
- }
- #endif
-
- CPU_CRITICAL_ENTER(); (1)
- if (en != DEF_ENABLED) { (2)
- OSSchedRoundRobinEn = DEF_DISABLED;
- } else {
- OSSchedRoundRobinEn = DEF_ENABLED;
- }
-
- if (dflt_time_quanta > (OS_TICK)0) { (3)
- OSSchedRoundRobinDfltTimeQuanta = dflt_time_quanta;
- } else {
- OSSchedRoundRobinDfltTimeQuanta = (OS_TICK)(OSCfg_TickRate_Hz / (OS_RATE_HZ)10);
- }
- CPU_CRITICAL_EXIT();
- *p_err = OS_ERR_NONE;
- }
- #endif
复制代码 1. 由于这几个参数是全局变量,所以必须关闭中断。
2. 根据形参设置是否使能时间片调度。
3. 变量OSSchedRoundRobinDfltTimeQuanta是用来设置默认的时间片个数,也就是说,如果程序中没有单独配置任务的时间片个数,就会使用这个默认时间片个数。
11.11.2 放弃剩余时间片OSSchedRoundRobinYield ()
这个函数的主要功能就是任务在完成工作的情况下,如果还有剩余的时间片,可以放弃这些时间去执行另外的同优先级任务(切记,是另外的同优先级任务)- /*
- ***********************************************************************************************************
- * 当任务不再需要剩余时间片时放弃CPU使用权
- *
- * 功能说明: 当任务的时间片还没用完的时候,可以通过此函数放弃剩余时间片,也就是放弃CPU使用权。
- * 形 参: p_err 错误代码指针变量
- * OS_ERR_NONE 函数调用成功
- * OS_ERR_ROUND_ROBIN_1 此优先级下只有这一个任务,能够放弃CPU权
- * OS_ERR_ROUND_ROBIN_DISABLED Round Robin调度没有使能
- * OS_ERR_SCHED_LOCKED 调度器被锁
- * OS_ERR_YIELD_ISR 不能在中断服务程序中调用此函数
- * 返 回 值: 无
- * 注 意: 1) 此函数必须在任务中调用
- ***********************************************************************************************************
- */
-
- #if OS_CFG_SCHED_ROUND_ROBIN_EN > 0u
- void OSSchedRoundRobinYield (OS_ERR *p_err)
- {
- OS_RDY_LIST *p_rdy_list;
- OS_TCB *p_tcb;
- CPU_SR_ALLOC();
-
-
-
- #ifdef OS_SAFETY_CRITICAL
- if (p_err == (OS_ERR *)0) {
- OS_SAFETY_CRITICAL_EXCEPTION();
- return;
- }
- #endif
-
- #if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u
- if (OSIntNestingCtr > (OS_NESTING_CTR)0) { /* 不能在ISR中调用此函数 */
- *p_err = OS_ERR_YIELD_ISR;
- return;
- }
- #endif
-
- if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { /* 调度器被锁不能执行此函数 */
- *p_err = OS_ERR_SCHED_LOCKED;
- return;
- }
-
- if (OSSchedRoundRobinEn != DEF_TRUE) { /* 确认round-robin已经使能 */
- *p_err = OS_ERR_ROUND_ROBIN_DISABLED;
- return;
- }
-
- CPU_CRITICAL_ENTER();
- p_rdy_list = &OSRdyList[OSPrioCur]; /* 此优先级下仅有这一个任务 */ (1)
- if (p_rdy_list->NbrEntries < (OS_OBJ_QTY)2) {
- CPU_CRITICAL_EXIT();
- *p_err = OS_ERR_ROUND_ROBIN_1;
- return;
- }
-
- OS_RdyListMoveHeadToTail(p_rdy_list); /* 移动当前任务的TCB到列表末 */ (2)
- p_tcb = p_rdy_list->HeadPtr; /* 获取列表首端的TCB */
- if (p_tcb->TimeQuanta == (OS_TICK)0) { /* 是0,使用默认的时间片数 */ (3)
- p_tcb->TimeQuantaCtr = OSSchedRoundRobinDfltTimeQuanta;
- } else {
- p_tcb->TimeQuantaCtr = p_tcb->TimeQuanta; /* 加载新的时间片数 */
- }
-
- CPU_CRITICAL_EXIT();
-
- OSSched(); /* 运行调度器 */ (4)
- *p_err = OS_ERR_NONE;
- }
- #endif
复制代码 1. 获取此优先级的就绪链表。从而得到此优先级下任务的个数,如果同优先级下只有一个任务,将退出这个函数。
2. 移动同优先级就绪链表中任务的位置,从实现同优先级下任务的切换。
3. 参数p_tcb->TimeQuanta = 0的时候就会使用默认的时间片个数,如果非0,就会给这个任务的时间片计数器赋予相应的时间片个数。
4. 执行任务调度。
11.11.3 Round-Robin调度算法OS_SchedRoundRobin()
当多个任务有相同的优先级时,μCOS-III允许任务在切换到另一个任务前运行特定的时间,也就是大家常说的时间片。这个过程就是Round-Robin调度或者时间片调度。如果任务不需要将所有的时间片用完,可以调用上面讲的函数OSSchedRoundRobinYield (),放弃剩余时间片从而切换到同优先级的另一个任务。μCOS-III支持用户在系统运行过程中使能或者禁止时间片调度,同时也支持全局的时间片设置,也支持每个任务的单独设置。
为了更好的说明Round-Robin调度算法,下面举一个例子(截图来自官方书籍):Task #1,Task #2,Task#3都运行在优先级 X,任务运行的时间片个数都是4。
1. 一开始是Task #3在运行,运行期间每个嘀嗒定时器中断都会让Task #3的时间片计数减一。
2. 第四次进入嘀嗒定时器中断后,Task #3的4个时间片已经用完。
3. 切换到同优先级就绪链表中下一个任务Task #1。
4. Task#1开始运行直到时间片用完。
5. 切换到Task #3运行。
6. Task#3运行一段时间后,调用函数OSSchedRoundRobinYield ()放弃剩余时间片。
7. 切换到Task #1运行。
8. 这里要特别注意:Task #1会运行4个时间片,图片上面画的不是很准确。
有了上面基础后,在解析一下相关函数。- /*
- ***********************************************************************************************************
- * 运行ROUND-ROBIN 调度算法
- *
- * 函数说明: 此函数每个嘀嗒定时器中断被调用,通过此函数来计算同优先级中是否有任务要执行。
- * 形 参: p_rdy_list 就绪链表指针
- * 返 回 值: 无
- * 注 意: 1) 内部函数调用,用户程序不能调用
- ***********************************************************************************************************
- */
-
- #if OS_CFG_SCHED_ROUND_ROBIN_EN > 0u
- void OS_SchedRoundRobin (OS_RDY_LIST *p_rdy_list)
- {
- OS_TCB *p_tcb;
- CPU_SR_ALLOC();
-
-
-
- if (OSSchedRoundRobinEn != DEF_TRUE) { /* 确认round-robin调度已经使能 */ (1)
- return;
- }
-
- CPU_CRITICAL_ENTER();
- p_tcb = p_rdy_list->HeadPtr;
-
- if (p_tcb == (OS_TCB *)0) { (2)
- CPU_CRITICAL_EXIT();
- return;
- }
-
- if (p_tcb == &OSIdleTaskTCB) { (3)
- CPU_CRITICAL_EXIT();
- return;
- }
-
- if (p_tcb->TimeQuantaCtr > (OS_TICK)0) { /* 时间片计数减一 */ (4)
- p_tcb->TimeQuantaCtr--;
- }
-
- if (p_tcb->TimeQuantaCtr > (OS_TICK)0) { /* 时间片还没有用完 */ (5)
- CPU_CRITICAL_EXIT();
- return;
- }
-
- if (p_rdy_list->NbrEntries < (OS_OBJ_QTY)2) { /* 是否只有一个同优先级任务 */ (6)
- CPU_CRITICAL_EXIT(); /* 是的,退出 */
- return;
- }
-
- if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { /* 调度器被锁,不能执行round-robin */ (7)
- CPU_CRITICAL_EXIT();
- return;
- }
-
- OS_RdyListMoveHeadToTail(p_rdy_list); /* 将当前的OS_TCB移到就绪链表的末尾 */ (8)
- p_tcb = p_rdy_list->HeadPtr; /* 获取修改后的就绪链表头部OS_TCB */
- if (p_tcb->TimeQuanta == (OS_TICK)0) { /* 检测是否需要使用默认的时间片 */ (9)
- p_tcb->TimeQuantaCtr = OSSchedRoundRobinDfltTimeQuanta;
- } else {
- p_tcb->TimeQuantaCtr = p_tcb->TimeQuanta; /* 加载新的时间片 */
- }
- CPU_CRITICAL_EXIT();
- }
- #endif
复制代码 1. 检测Round-Robin调度是否使能。
2. 确保此优先级下存在任务。
3. 这句话的意思是说:不允许用户将应用任务的优先级设置的和空闲任务优先级一样,也就是说空闲任务的优先级下不能有其它任务。
4. 时间片减一。
5. 任务的时间片还没有用完,退出继续执行。
6. 同优先级下必须有两个及其以上的任务才能继续往下执行。
7. 如果调度器被锁,不能执行Round-Robin调度。
8. 通过调整同优先级下的就绪链表获得下一个要执行的任务。
9. 判断使用全局的默认时间片个数还是使用单独设计的时间片个数。
11.11.4 函数使用举例
/* ********************************************************************************************************* * 函数名: main * 功能说明: 标准c程序入口。 * 形 参:无 * 返回值: 无 ********************************************************************************************************* */ int main(void) { OS_ERR err; OSInit(&err); /* 使能并设置全局时间片个数 */ OSSchedRoundRobinCfg(DEF_ENABLED, 8, &err); OSTaskCreate((OS_TCB *)&AppTaskStartTCB, (CPU_CHAR *)"App Task Start", (OS_TASK_PTR )AppTaskStart, (void *)0, (OS_PRIO )APP_CFG_TASK_START_PRIO, (CPU_STK *)&AppTaskStartStk[0], (CPU_STK_SIZE )APP_CFG_TASK_START_STK_SIZE / 10, (CPU_STK_SIZE )APP_CFG_TASK_START_STK_SIZE, (OS_MSG_QTY )0, (OS_TICK )0, (void *)0, (OS_OPT )(OS_OPT_TASK_STK_CHK |OS_OPT_TASK_STK_CLR), (OS_ERR *)&err); OSStart(&err); return (0); } /* ********************************************************************************************************* * 函 数 名: AppTaskCom * 功能说明: 此任务未使用 * 形 参:p_arg 是在创建该任务时传递的形参 * 返 回 值: 无 ********************************************************************************************************* */ static void AppTaskCOM(void *p_arg) { OS_ERR err; (void)p_arg; while(1) { …… OSSchedRoundRobinYield(&err); …… } } |