标签:
这一片谈谈关于ucos调度器的相关知识.
ucos的调度器的实现主要靠一个函数OS_Sched
该函数将调度器的行为分为了两个部分,第一是调度部分,第二是任务切换部分,如下
void OS_Sched (void)
{
#if OS_CRITICAL_METHOD == 3u
OS_CPU_SR cpu_sr = 0u;
#endif
OS_ENTER_CRITICAL();
if (OSIntNesting == 0u) {
if (OSLockNesting == 0u) {
OS_SchedNew();
OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
if (OSPrioHighRdy != OSPrioCur) {
#if OS_TASK_PROFILE_EN > 0u
OSTCBHighRdy->OSTCBCtxSwCtr++;
#endif
OSCtxSwCtr++;
OS_TASK_SW();
}
}
}
OS_EXIT_CRITICAL();
}
这段代码中使用了两个全部变量,分别是
OSIntNesting
OSLockNesting
第一个变量在系统中断的时候调用OSIntEnter使之+1,退出中断的时候使用OSIntExit使之-1,这样就能保证调度的时候系统中是没有中断的, OS_ENTER_CRITICAL这个代码关闭了中断,
第二个变量是一个调度锁,由OSSchedLock使之+1 OSSchedUnlock使之-1,用来在系统中短暂的屏蔽调度器的运行
此时可以看到,调度发生的时候,系统处于无中断而且调度器没有加锁的情况下,调用OS_SchedNew函数,该函数部分源码为
y = OSUnMapTbl[OSRdyGrp];
OSPrioHighRdy = (INT8U)((y << 3u) + OSUnMapTbl[OSRdyTbl[y]]);
还有部分源码也是相同的功能,只是63优先级扩展到255优先级,查找就绪表的过程变化了而已
OSPrioHighRdy表示了当前系统中准备好的最高优先级的任务的优先级,通过优先级找到最高优先级(准备好的)的系统tcb,并确保不是当前正在运行的任务的优先级,然后跳转到OS_TASK_SW进行任务切换
OS_TASK_SW是一个任务切换宏,展开来就是OSCtxSw函数,,是需要用户实现的一个接口代码,如下
OSCtxSw
PUSH {R4, R5}
LDR R4, =NVIC_INT_CTRL ;触发PendSV异常(causes context switch)
LDR R5, =NVIC_PENDSVSET
STR R5, [R4]
POP {R4, R5}
BX LR
这段代码出发了一个pend_sv的异常,那么应该就到了中断处理函数中
中断处理函数挺长的,这里就不贴代码了,但是其核心功能就是比较当前任务优先级和最高ready任务优先级,进行堆栈指针psp的切换和相应的赋值操作(tcbcur),当退出中断的时候就能实现任务切换了.具体来说,这个函数应当完成七个功能
把当前任务的端点指针保存到当前任务堆栈中
把处理器通用寄存器的内容保存到任务堆栈中
把被终止任务的任务堆栈指针当前值保存到该任务任务控制块的OSTCBSTKpTR中
获得待运行任务的任务控制块
是处理器通过任务控制块获得待运行任务的任务堆栈指针
将待运行任务的任务堆栈指针中的数据恢复到cpu他通过寄存器
使处理器获得待运行任务的断点指针
通过这里我们可以看到,如果一个任务占有最高优先级,而任务本身不对资源进行释放,也就是任务本身不去修改系统就绪表的话,这个任务将一直执行,除非出现更高级的任务.
说到这里,为什么要在终端中进行任务切换呢?原因在于,pc指针无法通过指令压入堆栈,但是能够在中断情况下cpu自动把pc压入堆栈,我们要获取pc指针,就只能通过堆栈了
以上所说的都是在正常情况下的任务切换,ucos在中断环境下也能进行任务切换,这个任务切换是通过函数OSIntExit函数来实现的,核心代码如下
if (OSRunning == OS_TRUE) {
OS_ENTER_CRITICAL();
if (OSIntNesting > 0u) {
OSIntNesting--;
}
if (OSIntNesting == 0u) {
if (OSLockNesting == 0u) {
OS_SchedNew();
OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
if (OSPrioHighRdy != OSPrioCur) {
#if OS_TASK_PROFILE_EN > 0u
OSTCBHighRdy->OSTCBCtxSwCtr++;
#endif
OSCtxSwCtr++;
OSIntCtxSw();
}
之前说到,退出中断的时候调用这个函数,将OSIntNesting-1,之后处理器会进行一次任务切换,别的地方都和OSSched()函数一致,只是任务切换的时候使用的切换函数是OSIntCtxSw函数,该函数原型如下
OSIntCtxSw
PUSH {R4, R5}
LDR R4, =NVIC_INT_CTRL ;触发PendSV异常 (causes context switch)
LDR R5, =NVIC_PENDSVSET
STR R5, [R4]
POP {R4, R5}
BX LR
NOP
和之前的OSCtxSw是一致的
经过上面的分析,知道了系统调度的具体过程,任务切换的时机就可以说是两个了,第一个是调用OSSched和中断中OSIntExit,而OSSched的调用在系统中哪些位置一查便知
多说一句,在不使用任务同步特性的情况下,最经常的造成任务调度的方法是OSTimeDly时钟延时的时候,任务调度的最终实现通过中断完成.
标签:
原文地址:http://www.cnblogs.com/dengxiaojun/p/4322480.html