- 合作式调度器为各种嵌入式系统提供了一个可预测的平台。
- 在一些场合,可能需要在合作式调度器结构中加热抢占式调度器的一些特性,并小心地加以控制。
从实用角度出发,合作式调度器也有局限性。尤其是在一些情况下,可能既需要运行长任务(例如每隔1000ms运行100ms时间),同时又需要运行频繁的短任务(例如每隔1ms运行0.1ms)。
这种结构综合了抢占式调度器和合作式调度器的特性,允许根据需求使(长)任务优先。然而,这种方式始终是受控的,不需要依赖复杂的上下文切换程序,以及任务间复杂的通信机制。
当已经建立了合作式调度器时,只需做非常少的程序改动就可以把它修改为混合式调度器。
在混合式调度器中,已经没有了(纯粹的)合作式的调度特性。这可能对设计过程及最终系统的可靠性带来深远的影响。
这里描述的混合式调度器形式和(纯粹的)合作式调度器区别如下:
- 不再要求所有任务都必须在时标间隔之间完成。一个(或更多)的合作式任务的运行时间可以大于时标间隔。
- 于前面讨论的合作式调度器一样,可以调度任意个合作式任务。然而,还可以同时调度一个抢占式任务。
- 抢占式任务抢先(中断)合作式任务。
- 一旦抢占式任务开始运行,将一直运行到完成。即,抢占式任务不能被任何合作式任务打断。抢占式任务可以看作是“最高优先权”的任务。
同时注意:
- 与完全的抢占式解决方案相比,只有一个抢占式任务,而且该任务连续运行直到完成,这将极大地简化系统结构。尤其是不需要实现上下文切换机制。这意味着:
- 该结构仍然非常简单
- 运行环境可以仍然全部由C来实现
- 和完全的抢占式平台相比,抢占式任务连续运行直到完成同样将简化任务间的通信
- 应该只有一个短任务(最长的运行时间大约为时标间隔的50%,尽可能的短)可以抢占运行,否则将削弱系统的总体性能。
混合式调度器“刷新”函数
typedef struct
{
void (*pTaskTypeDef)(void);
tWord Delay;
tWord Period;
tByte Co_op; //合作式任务,设为1; 抢占式任务,设为0
}sTask;
void hSCH_Update(void) interrupt
{
tByte Index;
for(Index=0; Index<HSCH_MAX_TASS; Index++)
{
//检测是否有任务
if(hsch_tasks_G[Index].pTask)
{
if(psch_tasks_G[Index].Delay == 0)
{
//有任务需要运行
if(hsch_tasks_G[Index].Co_op)
{
//合作式任务, RunMe标志加1
hsch_tasks_G[Index].RunMe += 1;
}
else
{
//抢占式任务, 立即运行
(*hsch_tasks_G[Index].pTask)();
if(hsch_tasks_G[Index].period == 0)
{
hsch_tasks_G[Index].pTask = 0;
}
}
if(hsch_tasks_G[Index].period)
{
//再次调度
hsch_tasks_G[Index].Delay = hsch_tasks_G[Index].period;
}
}
else
{
psch_tasks_G[Index].Delay--;
}
}
}
}
混合式调度器的最可靠使用方法
- 建立数量满足要求的合作式任务,很可能因为一个或多个任务的运行时间会大于时标间隔,所以需要使用混合式的调度器。混合式调度器的实现是安全的,然而必须保证任务不重叠。
- 实现一个抢占式任务。该任务一般(并非一定)将在每个时标间隔调用。这种任务的一种不错的应用是用来检查错误或紧急事件。因此,即使系统的主要用途是运行一个1000ms的合作式任务,也可以用抢占式任务来保证系统能够在10ms内响应外部事件。
- 记住,抢占式任务能够中断合作式任务。如果有关键代码段,需要实现一种简单的锁定机制
- 抢占式任务必须简短(最长的运行时间大约为时标间隔的50%,应尽可能短),否则将极大削弱系统的总体性能。
-
优缺点
能够同时处理“很少发生的长任务”和“频繁发生的短任务”,而合作式调度器不能同时处理
- 如果按照指导使用,将是安全并且可预测的
-
相关模式和替代解决方案
合作式调度器
- SCU调度器(本地)
- SCC调度器
使用T2的混合式调度器(1ms时标)
sch51.c
//如果不需要错误报告,将这一行注释掉 //#define SCH_REPORT_ERRORS #ifdef SCH_REPORT_ERRORS //用来显示错误代码的端口,旨在报告错误时使用 #define ErrorPort P1 #endif
led_hyb.c
//两个任务公用这个端口P2 //sbit LED_short_pin P2^2; //将把它当作共享资源, 并“锁定”它 #define LED_long_port P2
main.c
//混合式调度器的演示程序 //HYBRID SCHEDULER #include "main.h" #include "2_01_12h.h" void main() { //设置调度器 hsch_init_t2(); led_short_init(); //添加"短"任务 //这是一个抢占式任务 hsch_add_task(led_short_update, 0, 1000, 0); //添加“长”任务 //这是一个合作式任务 hsch_add_task(led_long_update, 0, 20000, 1); //启动调度器 hsch_start(); while(1) { hsch_dispatch_task(); } }
2_01_12h.c ```c //这是用于标准8051/8052的一种混合式调度器
include “2_01_12h.h”
//任务队列(参见SCH51.c) extern sTaskH hSCH_tasks_G[hSCH_MAX_TASKS]; //错误代码 extern tByte Error_code_G;
void hSCH_Init_T2() { tByte i; for(i=0; i<hSCH_MAX_TASKS; i++) { hSCH_Delete_task(i); } //错误变量 Error_code_G = 0; //定时器设置T2 //.. }
void hSCH_Start() { EA = 1; }
void hSCH_Update(void) interrupt xxx { tByte Index; for(Index=0; Index<hSCH_MAX_TASKS; Index++) { //检测是否有任务 if(hsch_tasks_G[Index].pTask) { if(psch_tasks_G[Index].Delay == 0) { //有任务需要运行 if(hsch_tasks_G[Index].Co_op) { //合作式任务, RunMe标志加1 hsch_tasks_G[Index].RunMe += 1; } else { //抢占式任务, 立即运行 (*hsch_tasks_G[Index].pTask)(); // hsch_task_G[Index].RunMe -= 1; if(hsch_tasks_G[Index].period == 0) { hsch_tasks_G[Index].pTask = 0; } }
if(hsch_tasks_G[Index].period)
{
//再次调度
hsch_tasks_G[Index].Delay = hsch_tasks_G[Index].period;
}
}
else
{
psch_tasks_G[Index].Delay--;
}
}
}
}
- hSCH51.h
```c
#ifndef HSCH51_H
#define HSCH51_H
//公共数据类型声明
typedef data struct{
void (*pTask)();
tWord Delay;
tWord Period;
tByte RunMe;
tByte Co_op;
}sTaskH;
//公共函数声明
void hSCH_Dispatch_Tasks();
tByte hSCH_Add_Task(void(*)(), tWord, tWord, tByte);
tByte hSCH_Delete_Task(tByte);
void hSCH_Report_Status();
//公共常数
#define hSCH_MAX_TASKS (2) //根据项目需求调整
#endif
- hSCH51.c ```c //混合式调度器内核 //任务队列 sTaskH hSCH_tasks_G[hSCH_MAX_TASKS]; //错误代码 tByte Error_code_G; // static void hSCH_GoTo_Sleep(); //记录上一次错误以来的时间 static tWord Error_tick_count_G; //上一次的错误代码 static tByte Last_error_code_G;
void hSCH_Dispatch_Tasks()
{
tByte Index;
for(Index=0; Index
tByte hSCH_Add_Task(void (*Fn_p)(), tWord Del, tWord Per, tByte co_op) { tByte Index=0; //在队列中找到一个空隙 while(hSCH_tasks_G[Index].pTask!=0 && Index<hSCH_MAX_TASKS) { Index++; } //是否到达队列尾部 if(Index==hSCH_MAX_TASKS) { //队列满 Error_code_G = ERROR_SCH_TOO_MANY_TASKS; return hSCH_MAX_TASKS; } //队列中有空隙 hSCH_tasks_G[Index].pTask = Fn_p; hSCH_tasks_G[Index].Delay = Del; hSCH_tasks_G[Index].Period = Per; hSCH_tasks_G[Index].Co_op = co_op; hSCH_tasks_G[Index].RunMe = 0; return Index; //返回任务的位置(以便以后删除) }
tByte hSCH_Delete_Task(tByte Task_index) { tByte Return_code; if(hSCH_tasks_G[Task_index].pTask == 0) { //没有任务 Error_code_G = ERROR_SCH_CANNOT_DELETE_TASKS; Return_code = RETURN_ERROR; } else { Return_code = RETURN_NORMAL; } //删除任务 hSCH_tasks_G[Index].pTask = 0; hSCH_tasks_G[Index].Delay = 0; hSCH_tasks_G[Index].Period = 0; hSCH_tasks_G[Index].Co_op = 0; hSCH_tasks_G[Index].RunMe = 0; return Return_code; }
void hSCH_Report_Status() {
ifdef SCH_REPORT_ERRORS
//只在需要报告错误时适用
if(Error_code_G != Last_error_code_G)
{
//检查新的错误代码
Last_error_code_G = Error_code_G;
if(Error_code_G != 0)
Error_tick_count_G = 60000;
else
Error_tick_count_G = 0;
}
else
{
if(Error_tick_count_G != 0)
{
if(--Error_tick_count_G == 0)
{
//复位错误代码
Error_code_G = 0;
}
}
}
endif
}
void hSCH_GoTo_Sleep() { PCON |= 0x01; //进入空闲模式 }
- LED_Hyb.c
```c
//用于混合式调度器的简单的“闪烁LED”的测试函数
//具有锁定机制
//用于锁定机制
#define LOCKED 1
#define UNLOCKED 0
//
static tByte LED_short_state_G;
//锁定标志
static tByte LED_lock_G = UNLOCKED;
void LED_Flash_Init()
{
//准备闪烁LED
}
void Flash()
{
if(LED_lock_G == LOCKED)
{
return;
}
//端口空闲--锁定它
LED_lock_G = LOCKED;
for(i=0; i<5; i++)
{
LED_long_port = 0x0f;
Hardware_Delay_T0(1000);
LED_long_Port = 0xf0;
Hardware_Delay_T0(1000);
}
//放开端口
LED_lock_G = UNLOCKED;
}