void OSStartHighRdy(void)
{
OSTaskSwHook();
OSRunning = TRUE;
_asm{
mov ebx, [OSTCBCur] ;OSTCBCur结构的第一个参数就是esp
mov esp, [ebx] ;恢复堆栈
popad ;恢复所有通用寄存器,共8个
popfd ;恢复标志寄存器
ret ;ret 指令相当于pop eip 但保护模式下不容许使用eip
;永远都不返回
}
}
void OSCtxSw(void)
{
_asm{
lea eax, nextstart ;任务切换回来后从nextstart开始
push eax
pushfd ;标志寄存器的值
pushad ;保存EAX -- EDI
mov ebx, [OSTCBCur]
mov [ebx], esp ;把堆栈入口的地址保存到当前TCB结构中
}
OSTaskSwHook();
OSTCBCur = OSTCBHighRdy;
OSPrioCur = OSPrioHighRdy;
_asm{
mov ebx, [OSTCBCur]
mov esp, [ebx] ;得到OSTCBHighRdy的esp
popad ;恢复所有通用寄存器,共8个
popfd ;恢复标志寄存器
ret ;跳转到指定任务运行
}
nextstart: //任务切换回来的运行地址
return;
}
这个函数是用于任务切换的主要函数。在任务发生切换的时候,首先将函数执行到的地址或者说OSCtxSw退出时将要返回的地址保存起来,压入栈中,然后将所有的标志寄存器、通用寄存器压入栈中,然后将程序的指针保存到OSTCBCur中,下一次当该任务获得执行权力的时候,就可以将保存的程序指针(esp)、通用寄存器、标志寄存器、程序执行到的位置(这里是nextstart),从栈中弹出来。这个过程同样是发生在OSCtxSw函数中,就是第二段汇编代码。可以想到,当程序再次获得执行权力的时候,就会将所有的一切从该任务的OS_TCB保留的栈中弹出来。
据我所知,程序的局部变量和调用子函数时的栈也是保存在栈中的,这个函数为什么没有将局部变量也保存起来呢?其实局部变量一直都在每个任务对应的OS_TCB指向的任务栈中,无需保存,这个是程序在执行的时候由esp(栈指针)直接保存的。那程序在执行的时候是如何保证每个任务的局部变量保存到应用程序为任务分配的栈中的呢?因为每个任务在获得执行权力的时候,都通过mov指令将esp指向了每个任务栈的栈顶,而这个栈顶就保存了每个任务栈的栈顶指针,无论是任务刚刚经过创建之后(OS_CreateTask(){OS_TCBInit(){ptcb->OSTCBStkPtr= ptos; }}),还是任务被挂起之后(第一段汇编语言),都会将栈顶指针保存在栈顶。当任务执行开始之后,其中创建的局部变量以及由于调用子函数而保存的栈都会保存到任务栈中,这是由于任务执行的时候esp指针是指向任务自己的任务栈的。而这个栈指针的生长是底层硬件决定的,会自动更改,从而任务栈也自动生长。
下面是任务堆栈的初始化函数,除了栈顶、还有task和SW之外,其他的用於通用寄存器的很多都是可以随便赋值的,因为在任务开始执行的时候将这些值弹入到通用寄存器后,程序在首次执行的时候会将通用寄存器中的值覆盖掉。stk是栈顶指针,当任务首次被执行的时候,会将其pop到esp中,因此不能随意赋值。而第一个*--stk的值是指向函数传入的参数,当然,这个是和硬件相关的,ARM的就不是这样的,而是传递给R0寄存器。
OS_STK *OSTaskStkInit (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT16U opt)
{
INT32U *stk; //console 下寄存器为32位宽
opt = opt; /* 'opt' is not used, prevent warning */
stk = (INT32U *)ptos; /* Load stack pointer */
*--stk = (INT32U)pdata; /* Simulate call to function with argument */
*--stk = (INT32U)0X00000000; //子程序是从当前esp+4处取得传入的参数,所以此处要空出4个字节
*--stk = (INT32U)task; /* Put pointer to task on top of stack */
*--stk = (INT32U)0x00000202; /* EFL = 0X00000202 SW 设置为中断开启 */
*--stk = (INT32U)0xAAAAAAAA; /* EAX = 0xAAAAAAAA */
*--stk = (INT32U)0xCCCCCCCC; /* ECX = 0xCCCCCCCC */
*--stk = (INT32U)0xDDDDDDDD; /* EDX = 0xDDDDDDDD */
*--stk = (INT32U)0xBBBBBBBB; /* EBX = 0xBBBBBBBB */
*--stk = (INT32U)0x00000000; /* ESP = 0x00000000 */
*--stk = (INT32U)0x11111111; /* EBP = 0x11111111 */
*--stk = (INT32U)0x22222222; /* ESI = 0x22222222 */
*--stk = (INT32U)0x33333333; /* EDI = 0x33333333 */
return ((OS_STK *)stk);
}