【深入实践ucos-ii】上下文切换机理

问题引入

上一篇博客给出了第一个ucos-ii的例程,再来看看两个任务程序代码:

/* App1Task */
void App1Task(void *p_arg)
{
    p_arg = p_arg;
    while(1)
    {
        printf("this is App1task!\n");
        OSTimeDlyHMSM(0, 0, 3, 0); /* 任务调度*/
    }
}

/* App2Task */
void App2Task(void *p_arg)
{
    p_arg = p_arg;
    while(1)
    {
        printf("this is App2task!\n");
        OSTimeDlyHMSM(0, 0, 2, 0); 
    }
}

CPU是怎么保证两个任务的死循环井然有序的执行的呢?这就涉及到程序运行的环境相关知识,关键就在OSTimeDlyHMSM这个函数,在执行该函数时CPU执行了上下文切换(也就是任务调度)。

上下文,也就是指两个任务所处的运行环境。

原理探索:

寄存器解释

这里我拿TI公司的某款16位的CPU来举例,从它的数据手册中查到它的寄存器如下图:

我们看到它有16个寄存器。

R0寄存器存放的是PC指针,CPU即将运行哪一条指令是由PC指针决定的,一般情况下CPU每运行一条指令后PC指针自动加1(对于8位的CPU是加1个字节,对于16位的CPU是加2个字节,对于32位的CPU是加4个字节,64位的CPU就是加8个字节啦)。如下关系图:

R1同时又是栈指针SP,我们知道一个函数在调用另一个函数时,必须进行圧栈操作保存当前PC值,以便函数返回时,弹出SP中保存的PC值,使CPU接着运行。如图:
【深入实践ucos-ii】上下文切换机理_第1张图片
当然对于ARM构架的芯片还有LR链接寄存器,它保存了函数返回地址,如果函数中出现了嵌套调用,那么LR的值就要入栈了。

数据手册图解:
【深入实践ucos-ii】上下文切换机理_第2张图片

R2/SR状态寄存器:SR状态寄存器保存了当前CPU在计算时的进位、借位、中断使能、始终发生器开关、CPU开挂等等状态。

R3/CG2常数生成器:通常不关心,我没深究。

R4-R15:通用寄存器。保存CPU在计算时临时存放的一些值。

程序运行环境

以上寄存器保存了程序运行时的一些状态、计算中间值、SP、PC等等,也即程序运行环境。

上下文切换

上下文切换就是CPU从一个程序运行环境切换到另一个程序运行的环境。
怎么切换呢?
首先,每个程序都有自己的任务栈,它里面预留了空间来保存PC指针、SP指针、LR指针、通用寄存器等等寄存器的值。在进行上下文切换时,首先将当前程序的运行环境保存到对应的任务栈中——称为入栈,再从另一个任务栈中调出一个运行环境给CPU执行——称为弹栈。这样就进行了任务切换。当然,整个切换过程不可以被其他中断打断,否则容易出错,ucos-ii的做法是在进行上下文切换前关闭一切中断,上下文切换后再打开中断。

图解:

任务切换是个非常频繁的操作,一般来说操作系统的这部分代码都是汇编语言写成的,这样是为了保证运行效率。

好了,上下文切换机理就讲到这儿了!

你可能感兴趣的:(操作系统,嵌入式,ucos-II)