微型抢占式多任务实时内核设计
2 任务管理
2.1 任务控制块
多任务系统中用任务控制块(TCB)来记录任务的各种属性。在这些属性中,最重要的是任务堆栈栈顶地址。进行上下文切换(context switch)时,被停止执行的任务的所有寄存器状态、下一条代码的地址都要入栈保护,因而这个属性是必需的。如果允许修改任务的优先级,优先级属性也是必需的。所以,将任务控制块简化如下:
typedef struct{
uint_16 msg[2]; /*消息接收区*/
int * sp; /*堆栈栈顶指针*/
uchar priority; /*静态优先级*/
uchar reserved; /*保留 */
}TCB,*PTCB;
TCB os_tcbs[ USER_TASK_NUM +1 ];
/*用户任务数最多为15个*/
msg用来存储发送给任务的消息,两个16位的二进制可按位存放32个消息。sp指向任务堆栈栈顶。priority记录任务的静态优先级。数组os_tcbs用来记录系统所有任务的信息,其下标与任务的ID号相对应,即ID号为N的任务的控制块为os_tcbs[N]。
2.2 任务的创建
os_CreateTask函数用来创建一个任务:
void os_CreateTask(
TASKPROC task, //任务函数的指针
uchar taskId, //任务的ID号
uchar priority, //优先级
int * pStack, //任务堆栈栈底地址
void * param //任务函数的入口参数
);
typedef void (*TASKPROC)( void * param);
创建任务时,内核要做以下几方面的工作:① 初始化任务控制块;② 初始化任务堆栈,使其如同被其它任务抢断时的情形;③ 将任务状态置为就绪态。该函数是依赖于处理器的,图1是较为通用的描述。
中断程序中,在高优先级任务剥夺低优先级任务之前,内核将断点时的各寄存器状态入栈保护,这部分区域即为寄存器映像区。将任务退出函数os_Exit的地址先于任务函数MyTask入栈,以使MyTask函数退出后返回到os_Exit中去,由此来实现任务的自动删除。
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)。当有紧急消息发送给任务时,内核会提升任务的动态优先级,从而提高消息处理的实时性。当任务无紧急