引 言
Small RTOS5l是一款专门为80C5l系列单片机设计的实时操作系统(实际上应该称其为实时内核),大部分代码用C语言编写,易于移植,十分适合于资源紧张的8位机。同时,它也是学习嵌入式操作系统原理极好的入门材料。本人就是在学习完SmallRTOS5l的基础上进一步学习了著名的uC/0S-II,受益颇多。
1 问题描述
在将Smau RTOS51应用于实验室某项目时,发现了一个奇怪的问题。简单说来,就是一个以无条件方式申请消息的任务竟然在没有取到消息的情况下CONTROL ENGINEERING China版权所有,以指示“等待超时”的代码返回了。
在这里,首先解释一下任务申请消息的两种方式:无条件方式和超时方式。所谓五条件方式是指任务申请消息时
假定有任务IDX以超时方式调用OSQPend()函数申请消息。OSQPend()函数首先会把IDX放到此消息队列的等待任务表中,然后再去判断队列中是否有消息。最佳情况是队列中确实有消息,则OSQPend()再把IDX从此消息队列的等待任务表中删除CONTROL ENGINEERING China版权所有,接着OSQPend()返回,任务取到消息。
此刻,假定消息队列中设有消息。那么,OSQPend()就会调用OSClearSigna1(OSRunningTaskID())和OS-Sched()这两个系统函数,迫使IDX进入休眠态,同时调度器调度下一个最高优先级的就绪任务来运行。假定任务IDY被选中,且IDY在运行中通过调用OSQIntPost()函数向此消息队列发送了一则消息。则OSIntPost()将把所有等待这个消息队列的任务中优先级最高的那个任
务唤醒控制工程网版权所有,并且把它从该消息队列的等待任务表中删除,假定它就是IDX。
当任务IDY进入休眠态后,操作系统才会调度IDX来运行。于是IDX从上次被强迫休眠的地方开始运行,即从OSQPend()函数中紧接着OSSched()的那条指令开始执行。具体来说,OSQPend()将首先查看IDX是否满足超时条件(用来判断任务是因为等待超时被唤醒的还是因为确实取到消息而被唤醒的),若超时时限尚未到达,OSQPend()再接着检查消息队列中是否已经有了消息。根据上面的假定,可以知道任务IDX确实是因为取到消息而被唤醒的。于是,OSQpend()把IDX从此消息队列的等待任务表中删除CONTROL ENGINEERING China版权所有,OSQPend()正常返回。这样,任务IDX取到消息,接着运行。
以上都没有什么问题,但是,有一种情况被忽略了,而正是这种情况的出现导致了任务IDX被长时间挂起,就算队列中有消息存在,IDX也无法被唤醒,只能等到其超时为止。
为讨论方便,不妨仍按上述假定情况来分析。当任务IDX被唤醒且IDY进入休眠状态后,系统必将调度下一个优先级最高的就绪任务来运行。在前面,认为这个任务就是IDX,然而此时,假定它是另一个比IDX优先级更高的任务IDZ(因为有可能是中断把IDZ唤醒的,所以中断退出时,操作系统强制IDY进入休眠态,转而调度IDZ运行)。非常巧合的是控制工程网版权所有,IDZ在运行的过程中向同一个消息队列也申请了消息。由于之前IDY已经向消息队列发送过一条消息,则IDZ将正常取到此条消息。于是,消息队列中的消息数减为O(Buf[0]==0)。在任务IDZ进入休 眠后,任务IDX被操作系统调入CPU运行。同样,函数OSQPend()首先查看IDX是否等待超时。如果没有超时再检查消息队列中是否存在消息。注意到先前已经假定消息被