ucos学习摘要(一)

1 引言

由于 C/OS在设计之初就充分考虑了本身在不同处理器上的移植问题,因此在任何处理器上的移植 C/OS都只需要关心三个文件:头文件OS_CPU.H 、C文件0S_CPU_C.C和汇编文件OS_CPU_A.ASM.下面我们分别由这三个文件入手来介绍移植需要解决的几点问题。

一. C/OS-II本身涉及到的问题

C/OS中的任务总是处于五种状态之一:睡眠态、就绪态、运行态、等待状态和中断服务态。任何任务必须首先创建处于就绪态之后才有可能运行,任务创建函数OSTaskCreate()和OSTaskCreateExt()会初始化任务的栈结构,使堆栈看起来就象刚刚发生过中断一样,所有寄存器保存在任务的堆栈之中若要任务恢复执行,只须在最后执行一条中断返回指令即可。这就是初始化堆栈时把堆栈看起来就绪刚刚发生过中断一样。

C/OS-II的移植文件OS-CPU-C.C中,唯一必要的函数就是0STaskStkInit()。该函数会在任务创建时被调用,用来初始化任务的堆栈结构。因此,在真正动手编写移植代码之前必须首先设计好任务的堆栈结构。使任务看起来象刚刚发生过中断一样。例如任务的中断结构如下:

       

 

在另一个移植文件OS-CPU-A.ASM中,需要改写四个函数,分别是OSSstartHighRdy(); OSCtxSW(); OSIntCtxSW()和OSTickISR()。

OSSstartHighRdy()由函数OSStart()函数调用,功能是让进入就绪态的优先级最高的任务运行,函数原型如下:

 

void OSStartHighRdy()

{

调用用户定义函数OSTaskSwHook();

OSRunning=TRUE; //第一部分START(1)

 

获取堆栈指针SP=OSTCBHighRdy->OSTCBStkPtr;

从新任务堆栈中恢复所有寄存器;

执行中断返回指令; //第二部分START(2)

}

OSCtxSW()是一个任务级的任务切换函数,OSIntCtxSW()是一个中断级的任务切换函数。区别是前者是在任务需要进行切换时被宏OS_TASK_SW()调用。而后者是在中断退出时被函数OSIntExit()调用的。一个服务于任务,一个服务于中断。这两个函数颇有相似之处。

OSCtxSW()函数原型如下:

void OSCtxSW(void)

{

保存处理器的寄存器到当前任务的堆栈中;

将当前任务的堆栈指针保存的该任务的OS_TCB中:

OSTCBCur->OSTCBStkPtr=堆栈指针; //第一部分:CTX(1)

 

调用用户定义函数OSTaskSwHook();

OSTCBCur=OSTCBHighRdy;

OSPrioCur=OSPrioHighRdy; //第二部分:CTX(2)

 

获取新任务堆栈指针SP=OSTCBHighRdy->OSTCBStkPtr;

从新任务堆栈中恢复所有寄存器;

执行中断返回指令; //第三部分:CTX(3)

}

 

OSIntCtxSW()函数原型如下:

void OSIntCtxSW(void)

{

调用用户定义函数OSTaskSwHook();

OSTCBCur=OSTCBHighRdy;

OSPrioCur=OSPrioHighRdy; //第一部分:INTCTX(1)

 

获取新任务堆栈指针SP=OSTCBHighRdy->OSTCBStkPtr;

从新任务堆栈中恢复所有寄存器;

执行中断返回指令; //第二部分:INTCTX(2)

}

由以上两函数的原型可见,OSIntCtxSW()比OSCtxSW()所少的部分就是CTX(1)部分。而该保存处理器寄存器的部分在OSIntCtxSW()函数中没有的原因是:OSIntCtxSW()函数是在中断中被调用的,而在中断的一开始就已经保存了处理器的寄存器。因此在该函数中,无须再次保存。

因此,在移植程序的设计中,可以把OSIntCtxSW()设计为一个子函数,而在OSCtxSW()函数中进行调用即可。同样,OSStartHighRdy()的START(2)部分、OSIntCtxSW()的CTX(3)和OSIntCtxSW 的INTCTX(2)执行同样的功能,因此,可以用同一段代码实现。

二.由ARM体系结构的特殊性带来的移植问题

问题一:由于ARM体系结构采取七中处理器模式和两种指令状态,由此带来了程序运行的模式选择问题和状态切换。在一般情况下,为了防止用户的应用程序随意访问更改系统资源,我们采用操作系统(ucos)来保护系统资源因此操作系统可以访问系统内的所有资源,而用户进程则会受到各种限制检查。ARM体系结构的七种处理器模式,除去五种异常模式外,就只剩下用户模式和系统模式。而每一种异常模式与相应的处理器异常对应(R13,R14分别对应于自己的处理器模式),不适合作为操作系统或用户进程的运行模式。

系统模式可以访问系统内部所有资源而不受任何限制,因此最适合做为操作系统的运行模式,用户进程则可以运行在用户模式下。在用户模式下,用户进程不能随意更改处理器的模式,因此也就限制了进程的访问权限,保护了系统资源

也正因为如此,用户进程工作于用户模式,当发生中断时系统自动进入中断模式,在中断的刚开始执行时,要进行现场保护,按照 C/OS的要求,此时应该把现场保护到用户进程的堆栈之中。这对于象8086这种一般处理器来说,并不算难事,但是对于ARM核芯片来说却要花费很大的开销。例如:

INT1( )

{

PUSH A;

PUSH ES;

PUSH DS //第一部分:INT(1)

……….

TaskCode( );

OSIntExit( );

 

POP DS;;

POP ES;

POP A; //第二部分:INT(2)

}

在执行INT(1)部分代码时,由于此时处理器处于中断模式,此时的压栈操作并没有把寄存器压入用户任务的堆栈之中。当执行OSIntExit( )进行任务切换时,由于用户任务的堆栈之中并没有相应的现场保护积存器,系统就会崩溃。

而如果在中断的开始阶段就进行模式切换,并把所有的寄存器保护到用户任务的堆栈之中,当执行OSIntExit( )时,却并不一定真正进行任务切换(因为软件中断时,首先要判别中断号,如果为0才进行任务切换),那么中断开始执行时的开销就会无端的浪费处理器的宝贵时间

INT2( )

{

//不进行模式转换到用户模式,暂时保存于中断模式堆栈之中

当需要实际进行切换时(当中断号为0时),再把寄存器保存到用户任务的堆栈之中

PUSH A;

PUSH ES;

PUSH DS //第一部分:INT(1)

 

……….

TaskCode( );

OSIntExit( ); //在OSIntCtxSw( )中进行保存寄存器于用户堆栈中的操作

 

POP DS;;

POP ES;

POP A; //第二部分:INT(2)

}

上面的编程方法可以很好的解决所存在的问题。

在中断的开始执行阶段不进行模式转换,仍然把寄存器暂时保存于中断模式堆栈之中。当调用OSIntExit( )时, C/OS会判断是否进行任务切换(中断号是否为0),若有更高的优先级任务就绪而需要进行任务切换时,在OSIntCtxSw( )中把寄存器保存到用户任务的堆栈之中。这样程序的执行就可以达到最优化。

问题二:此外,还存在一个细节问题。由ARM相关资料知,ARM处理器支持两种指令集合,分别是ARM指令和THUMB指令。因此,ARM处理器可能处于两种指令执行状态,而当移植函数OSIntCtxSw( )进行任务切换并恢复任务的运行环境时,由ARM规范知,我们不能通过直接给程序状态寄存器赋值来改变处理器的状态,而只能用异常返回指令恢复任务原来的运行状态

 

OSIntCtxSw()

{

………

LDMFD SP! , {R0-R12, LR,PC}^ //该指令只能在异常模式下使用,如果在用户模式或系统模式下使用该指令,会产生不可预知的结果

}

 

因此,我们必须首先使处理器进入一定的异常模式(这里用到管理模式,因为其他异常模式可能在下面会用到),然后,在该模式下恢复任务的运行环境,并返回到任务切换前的指令执行状态。

三.结束语

经过移植后的操作系统经过测试,运行稳定,并达到了实时系统的要求.

 

 

 

 

转载于:https://www.cnblogs.com/gmh915/archive/2009/09/30/1577019.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值