|
在建立一个任务时,需要进行堆栈初始化,将堆栈初始化看起来好像是刚刚发生过中断一样。在被初始化的堆栈中,分为 4 个部分,分别是:
模仿 PUSH DS 和 PUSH ES
模仿 PUSHA
模仿 中断( push PSW, push SEG task, push OFF task )
模仿任务的调用( push SEG pdata, push OFF pdata, push SEG task, push OFF task )
-------------------------------------------------------------------------------------------------------------
当任务被恢复开始运行的时候,当执行到 iret 这指令时,PSW,SEG task,OFF task 出栈,这时候就切换到了任务函数的领空。假设任务函数是:
task( void *pdata )
{
int temp = 10;
...
for(;;)
...
}
那么当任务开始运行时,是从 int temp = 10 这个地方开始运行的,也就是说此时 IP 寄存器的内容就是 int temp = 10 这条语句所对应汇编指令的地址。
这里,参数 void *pdata 进行怎么样的处理了呢?
假如按照一般函数的调用流程,这里假设,task( void *pdata )就是一个普通的 C 函数。那么当它被调用时,它的参数*pdata 就会先被入栈保存,接着是函数的返回地址入栈保存,最后真正的到达函数体中,也就是 int temp = 10 这条语句处。
然而,在这里,当我们切换到任务 task 时,似乎没看到 task 被保存起来,而是直接调到了int temp = 10 这里。
事实上,这一步工作正好是在对“任务堆栈初始化”时已经做了的---也就是“模仿任务的调用”部分。在这个部分里,已经模仿了编译器对任务函数调用后进行的“前期”工作。
所以,当这个任务在需要这个 *pdata 参数时,自然而然的就到它自己的堆栈中进行查找了。
下图是一个小程序反汇编后的截图:
在上图中,第一行,TaskStart(void *pdata)是自行建立的一个任务。
在右下角的堆栈观察窗口里,ss:1992 和ss:1990 正是对应着 *pdata 这个参数的段地址和偏移地址。
CPU窗口中第二行是进入函数体,在开辟两个临时变量 x,y 的堆栈空间。
有此可见,在切换到函数时,uc/os-ii 已经模仿了编译器对 *pdata 的保存,而 *pdata 所传进的实际参数也正是由堆栈初始化函数时被带进到任务的堆栈中。
--------------------------------------------------------
以上内容为自己的见解,不知道分析是否都正确了,如果哪位胸垫看后认为分析有错误的地方,请指正,将会很感激。
|
|