uc/os中OSSched()函数分析

本文深入分析UC/OS中的任务调度器,重点讲解OS_Sched函数,它负责找到最高优先级的任务并确保中断关闭以防止数据冲突。同时,介绍了任务切换的关键函数OS_TASK_SW()及其软中断处理过程,详细阐述了软中断触发后的处理器状态和堆栈操作。

OS_Sched()分析

         uc/os中总是运行优先级最高的就绪任务,确定哪个任务优先级最高,该由哪个优先级人物运行了,这一工作是由任务调度器完成的,(而具体的任务切换,是任务调度器在调用其他函数来完成)。其中任务级的调度由函数OS_Sched()来完成,中断级的调度由OSIntExt()来完成的。

OS_Sched()函数分析

void OSSched (void)

{

    INT8U y;

 

 

    OS_ENTER_CRITICAL();

    if ((OSLockNesting | OSIntNesting) == 0) {//判断是否满足调度条件,在uc/os中任务级调度的调用不允许来自中断服务子程序(OSIntNesting) == 0),此外当调度器上锁时,任务调度函数将直接退出,不做任务调度                                              

        y             = OSUnMapTbl[OSRdyGrp];                               

        OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]);//这两行代码是获得进入就绪态且优先级最高的任务                                          

        if (OSPrioHighRdy != OSPrioCur) {//检验优先级最高的任务是否是当前正在运行的任务。以避免不必要的的任务调度,毕竟任务调度是需要时间滴。                       

            OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];//通过当前最高优先级OSPrioHighRdy,从任务控制块优先级表中OSTCBPrioTbl[]获得当前最高优优先级任务控制块

            OSCtxSwCtr++;//该全局变量(32位)用于记录任务切换的次数,                                                        

            OS_TASK_SW();最后调用OS_TASK_SW宏来完成实际上的任务切换,该宏是一个软中断                                                                                                                      

        }

    }

    OS_EXIT_CRITICAL();

}

Ø  该函数调用所需要的时间是常量,于实际的任务数无关

Ø  os_sched()所有代码都是临界段代码,在准找进入就绪态的优先级最高任务时,为了防止中断服务子程序把多个任务的就绪位置位,中断是关闭的。

 

OS_TASK_SW()函数分析

任务切换的内容其实是很简单的:将被挂起任务的寄存器压入堆栈,然后将高优先级的寄存器从栈中恢复到CPU的寄存器中。该过程使用软中断来实现。

 

 

SoftwareInterrupt

        LDR     SP, StackSvc            ; 重新设置堆栈指针,在LPC2200开发板中软中断触发后,开发板处于管理模式

        STMFD   SP!, {R0-R3, R12, LR}       //压栈,在下面过程中使用到了以下以下的寄存器,故需要将这些寄存器压入管理模式堆栈,来实现原始寄存器的保存。

        MOV     R1, SP                  //R1指向参数存储位置

 

        MRS     R3, SPSR                /ARM处理器中,只有MRS指令可以读取状态寄存器

        TST     R3, #T_bit              //中断前是否是Thumb状态

        LDRNEH  R0, [LR,#-2]            //: 取得Thumb状态SWI

        BICNE   R0, R0, #0xff00

        LDREQ   R0, [LR,#-4]            //: 取得arm状态SWI

        BICEQ   R0, R0, #0xFF000000

                                        // r0 = SWI号,R1指向参数存储位置

        CMP     R0, #1//比较软中断号,确立软中断的服务程序

        LDRLO   PC, =OSIntCtxSw//0号软中断用于任务级切换

        LDREQ   PC, =__OSStartHighRdy   ; SWI 0x01为第一次任务切换

 

        BL      SWI_Exception//跳转到软中断服务子程序

       

        LDMFD   SP!, {R0-R3, R12, PC}^//SWI异常中断返回

       

StackSvc           DCD     (SvcStackSpace + SVC_STACK_LEGTH * 4 - 4)

Ø  对于SWI中断来说,其格式是SWI{cond} immed_24;后面的24位数字就是软中断号,故在SWI异常中断出路程序中,取出SWI中断号的方法是首先确定软中断SWI指令是ARM指令还是THUMB指令(通过访问SPSR获得),然后取得该指令的地址,可通过LR获得,接着读出指令,分解出立即数,该立即数就是软中断号。

Ø  在软中断触发后,处理器应进入管理模式,故需要从新设置堆栈指针。

Ø  在软中断触发后,SoftwareInterrupt相当于是一个判断程序,只是简单的取出中断号,具体的实现是调用其他函数完成的。

Ø  SoftwareInterrupt中,进行了一次压栈,是因为在获得软中断号的过程中使用了R0,R1,R2,R3故需要将这些寄存器压栈,而使用的SP是管理模式下的寄存器,其于用户模式下的SP寄存器不是一个寄存器,故不需要进行压栈。

 

任务级调度 OSIntCtxsw

OSIntCtxSw

                                                    //下面为保存任务环境

        LDR     R2, [SP, #20]                       //此时处理器仍处于管理模式,从管理模式堆栈中获得PC保存到R2

        LDR     R12, [SP, #16]                      //从堆栈中获取R12,更新CPU寄存器

        MRS     R0, CPSR                         //保存当前状态寄存器,用于模式之间的切换

 

        MSR     CPSR_c, #(NoInt | SYS32Mode)//进入系统模式,且关中断

        MOV     R1, LR           //获得系统模式下的LR保存到R1中,该LR就是原始任务的LR

        STMFD   SP!, {R1-R2}       //此时处理器处于系统模式,故SP是原始任务的堆栈指针,故原始任务的LR,PC压入原始任务堆栈

        STMFD   SP!, {R4-R12}      //由于R4-R11寄存器未改变,故其内容是原始任务的内容,直接压入原始任务堆栈即可。

 

        MSR     CPSR_c, R0 //此时切换回管理模式

        LDMFD   SP!, {R4-R7} //从管理模式堆栈中获得前面保存的原是任务的RO-R3,保存到CPUR4-R7                      

        ADD     SP, SP, #8                          //忽略掉R12,PC,使堆栈指针回到初始位置,使得下次继续使用

       

        MSR     CPSR_c, #(NoInt | SYS32Mode)//进入系统模式

        STMFD   SP!, {R4-R7}                       //原始任务的R0-R3入栈保存

       

        LDR     R1, =OsEnterSum               //获取OsEnterSum

        LDR     R2, [R1]

        STMFD   SP!, {R2, R3}            //从上面知道R3的内容为原始任务的CPSR,故该指令保存CPSR,OsEnterSum

 

        LDR     R1, =OSTCBCur  //获得原始任务的TCB地址

        LDR     R1, [R1]//该地址的内容是原始任务的SP

        STR     SP, [R1] //保存原始任务堆栈指针到原始任务的TCB

 

        BL      OSTaskSwHook                        //调用钩子函数

                                                    ;OSPrioCur <= OSPrioHighRdy将当前优先级切换为最高优先级(优先级的切换)

        LDR     R4, =OSPrioCur//当前TCB结构体首地址

        LDR     R5, =OSPrioHighRdy//最高优先级TCB结构体首地址

        LDRB    R6, [R5]

        STRB    R6, [R4]//更新当前TCB指向最高优先级TCB

                                                    ;OSTCBCur <= OSTCBHighRdy将当前TCB切换为最高优先级的TCB(TCB的切换)

        LDR     R6, =OSTCBHighRdy

        LDR     R6, [R6]

        LDR     R4, =OSTCBCur

        STR     R6, [R4]

OSIntCtxSw_1

                                                    ;获取新任务堆栈指针

        LDR     R4, [R6]//R6为新任务的TCB地址,其内容是新任务的堆栈指针

        ADD     SP, R4, #68 //;17寄存器CPSR,OsEnterSum,R0-R12,LR,SP,移动新任务堆栈的SP,指向栈底

        LDR     LR, [SP, #-8]//新任务LR进入CPU寄存器

        MSR     CPSR_c, #(NoInt | SVC32Mode)        //进入管理模式

        MOV     SP, R4           //设置堆栈指针

 

        LDMFD   SP!, {R4, R5}                       //CPSR,OsEnterSum

                                                   //恢复新任务的OsEnterSum

        LDR     R3, =OsEnterSum

        STR     R4, [R3]

   

        MSR     SPSR_cxsf, R5                       //恢复CPSR

        LDMFD   SP!, {R0-R12, LR, PC }^             ;新任务RO-R12LR,PC出栈,运行新任务

Ø  该函数执行前的数据结构:OSTCBCur指向原始任务(低优先级任务)、CPUSP指向原始任务的栈顶、OSTCBHighRdy指向新任务的TCB

Ø  【转】msr cpsr_cxsf,r1        ;这里的cxsf表示从低到高分别占用的48bit的数据域

指令中有时还有出现cpsr_cf, cpsr_all, cpsr_c等,这里:

        c
CPSR中的control field ( PSR[7:0])
        f
flag field (PSR[31:24])
        x
extend field (PSR[15:8])
        s
status field ( PSR[23:16])

其中cpsr的位表示为:
31 30 29 28 ---   7   6   -   4    3   2   1   0
N   Z   C   V         I   F       M4 M3 M2 M1 M0

                                       0     0   0    0   0     User26
模式
                                       0    0   0    0   1     FIQ26
模式
                                       0    0   0    1   0     IRQ26
模式
                                       0    0   0    1   1     SVC26
模式
                                       1    0   0 0   0     User
模式
                                       1    0   0   0   1     FIQ
模式
                                       1    0   0   1   0     IRQ
模式
                                       1    0   0   1   1     SVC
模式
                                       1    0   1   1 1     ABT
模式
                                       1    1 0    1    1     UND
模式

深入分析:
对于MSR(寄存器到状态寄存器)的指令,
        MSR CPSR,       r0
        MSR CPSR_all,   r0
        MSR CPSR_flg,   r0
都是已经过时的表示方法。
对于MRS(状态寄存器到寄存器)的指令,
        MRS R0,     CPSR       
等同于MRS R0, CPSR_cxsf
        MRS R0,     CPSR_all   
会有waring
        MRS R0,     CPSR_flg  
会有错误
ADS中使用c,f,x,s表示cpsr的各个部分是推荐的。从指令来说:
        MSR CPSR_f,         r0
机器码为0xe128f000
        MSR CPSR_c,         r0
机器码为0xe121f000
        MSR CPSR_x,         r0
机器码为0xe122f000
        MSR CPSR_s,         r0
机器码为0xe124f000
可见机器码中用bit[29:16]4bit表示是f,c,x,s的。所以能够在机器执行的时候,
给予不同的执行结果。为了代码向后兼容性,建议使用f,c,x,s尾缀

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值