首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

ucos在s3c2410上运行过程整体剖析--创建任务到多任务调度及运行(5)

ucos在s3c2410上运行过程整体剖析--创建任务到多任务调度及运行(5)

那就把Task1这个函数指针赋给PC和LR。接着是R1~R12这些通用寄存器,由于函数还没有执行过,这些通用寄存器的值是什么就不太重要,可以随便赋值,你看,这里就是给R1赋值1,给R2赋值2 …………  给R12赋值12。当然你也可以给这些寄存器赋其他值,这些无关紧要,但当任务运行过后,那再保存程序执行现场时就要按章程来了,即这些寄存器被切换的时候里面的值是什么就应该保存什么。下面就要初始化CPSR和SPSR了,这两个值要根据你的操作系统要运行在处理器的那种模式下,任务运行时应该开中断的。你像我们这个把CPSR的值赋成SUPMODE(这个宏的值是0x13),就是说这个任务运行时在SVC模式下并且开中断。咱们原来说过ucos初始化过程CPSR的中断一直是关着的,CPSR的中断位就是当最高优先级任务运行时就已经开中断了。
一句话说那,现在初始化任务堆栈就是未雨绸缪。
所以,“初始化堆栈”实际上是做了一个“未雨绸缪”的工作。这个过程中有两点是必须慎重考虑的,一是PC该如何定位,二是CPU的其它寄存器(除PC之外)该怎么处理。先说第一点,因为任务是第一次运行,而任务从实质上将就是一段代码,所以PC指针应该定位到这段代码的第一行处,即所谓的入口地址(Entry Point)处,这个地址由任务指针保存着,所以把该指针值赋给PC即可。第二,这段代码还未被履行过,所以代码中的变量与CPU的其它寄存器一点关系也没有,因此R0-R12,R14可随便给值,或者不赋值也可,让这些寄存器保持原来的值,显然后者更为简单。最后再给CPSR赋值,用户可以根据实际需要使系统运行于系统模式或管理模式。

关于初始化任务控制块的代码,我想我们有了前面的有关TCB的相关知识应该很容易就会理解。我们说我们创建了一个任务,其唯一标识就是新创建了一个任务控制块。对这个任务控制块的初始化操作就是对里面的各个选项赋予相应的值而已。至于每一个选项的具体意义,源码里都有详细的说明,要是不懂可以参考邵贝贝翻译的书。

任务创建完成后,要把这个任务置为就绪态。关于怎么置位就绪表是怎么实现的,我也不想多说,前面介绍的就绪表的实现原理要仔细看。你懂了原理,实现就是用c语言的位操作相关的知识。包括或操作、与操作等,自己看源码哦!

现在我们已经知道了创建任务的方法。
我们假设我们共创建了4个任务,一个系统级任务,两个用户任务,还有一个空闲任务。

下面我们就说明一下,这个ucos是怎么开始多任务调度的。
这个系统级任务,就是任务优先级最高的任务,ucos一开始调度会找到优先级最高的任务并开始运行,这个系统级任务有它自己的特殊任务,就是一,把OSRunning置成TRUE,标志ucos开始调度运行。二:开启定时器,并开启定时器中断响应。

这是最系统级任务的运行代码:
static void SYS_Task(void *Id)
{
       OSRunning=TRUE;       //begin OS

       uHALr_InstallSystemTimer();

       printk("start system task.\n");
       for (;;)
       {
              OSTimeDly(1000);
       }
}
不过,创建完这个任务并不会执行,而是等到ucos操作系统调度到这个任务时,它才开始真正运行。
我们创建任务完毕后,会调用OSStart,多任务调度开始函数

void  OSStart (void)
{
    INT8U y;
    INT8U x;


    if (OSRunning == FALSE) {
        y             = OSUnMapTbl[OSRdyGrp];  /* Find highest priority's task priority number   */
        x             = OSUnMapTbl[OSRdyTbl[y]];
        OSPrioHighRdy = (INT8U)((y << 3) + x);
        OSPrioCur     = OSPrioHighRdy;
        OSTCBHighRdy  = OSTCBPrioTbl[OSPrioHighRdy]; /* Point to highest priority task ready to run    */
        OSTCBCur      = OSTCBHighRdy;
        OSStartHighRdy();                  /* Execute target specific code to start task     */
    }
}
这个函数的主要任务就是找到最高优先级的任务的控制块,然后根据创建任务时的堆栈内容,把堆栈里的内容恢复到各个寄存器中,那这最高优先级的任务就开始真正运行了。这时ucos操作系统就掌握了整体的运行了。

关于怎么找到的最高优先级,是根据OSRdyGrp和OSRdyTbl[]这两个变量,至于方法,我们在就绪表的相关内容一节已经讲解。知道了最高优先级就知道了最高优先级任务的TCB,知道了TCB就得到了任务堆栈。把此任务的任务堆栈恢复到各个寄存器,这个任务就开始真正运行了。
OSStartHighRdy();  这个函数是用汇编写的,很简单就是恢复寄存器现场。
OSStartHighRdy
         LDR r4, addr_OSTCBCur    ; Get current task TCB address
         LDR r5, addr_OSTCBHighRdy     ; Get highest priority task TCB address

         LDR r5, [r5]                 ; get stack pointer
         LDR sp, [r5]                ; switch to the new stack

         STR  r5, [r4]                 ; set new current task TCB address

         LDMFD    sp!, {r4}             ; YYY
         MSR SPSR_cxsf, r4
         LDMFD    sp!, {r4}             ; get new state from top of the stack
         MSR CPSR_cxsf, r4              ; CPSR should be SVC32Mode
         LDMFD    sp!, {r0-r12, lr, pc }     ; start the new task
代码要自己好好分析,不要调皮哦。
好了,现在已经是优先级最高的任务开始运行了,它要干什么那?前面已经把代码贴出来了。
下面就说说这个定时器,哈。

前面说了,这个ucos操作系统的核心就是定时器,定时器就是ucos的心脏。

进程切换的原因很多,如一个进程要等待一个资源但资源还没有出现,那这个进程要被挂起,让另一个进程来执行。我们说一个最普遍的引起任务切换的原因。时间,在操作系统里有很多控制都是基于时间的。比如,你一个任务执行时什么资源都不缺少,那我可不能让你一直运行下去,你一直运行下去的话,就违背了的操作系统设计的初衷。所以任何任务都有时间限制。时间一到就进行切换。那时间谁记那?当然是定时器了,定时器隔固定的时间产生相应的中断来提示操作系统运行了多长时间。嘻嘻,时钟中断就好像操作系统的心脏跳动一样。

在我们现在的状态下,已经创建的任务除了处理器之外不用等待其他资源,任务主动放弃cpu的使用权是产生调度的一个原因,这时候发生的是任务级任务切换。比如在ucos中有一个任务延时函数,这个函数会让当前任务挂起并延时指定的时间。那谁记录这些时间那,当然是定时器,当时间到了会产生任务切换,当然这时候是中断级的任务切换。

既然定时器如此重要,我们看看ucos是怎么利用定时器的中断服务程序来实现这些功能的。

首先,定时器中断的入口是arm的外部中断入口。当定时器定时时间到时,就产生定时器中断,通过判断中断定时器的内容来跳转到指定的中断服务程序里去执行。至于很多中断源都通过一个入口,怎么区分到底是哪个中断来了那,这是arm中断控制器的相关章节内容。Datasheet第十四章 interrupt controllor 。
继承事业,薪火相传
返回列表