mykernel开源项目见于github:mykernel
假设有如下任务结构:任务A和任务B:
void a()
{
……
};
void b()
{
……
if(当前处于B任务)
Schedule到A;
……
};
void c()
{
……
};
任务A
{
a();
Schedule到B;
b();
c();
};
任务B
{
b();
};
首先:一个任务只能有一个上下文环境,即便是反复被调度。
有如下推断:
-
首先,A运行,然后执行
Schedule到B;
调度到B,A的运行环境保存(第一次保存A任务上下文),恢复为B的运行环境,B运行。然后调度到A的时候,B的运行环境保存,恢复为之前A的运行环境。 -
A接着上次的位置调用函数
b()
,此时就是函数嵌套调用了。那么,在A的任务栈保存ebp,esp,返回地址(所有的这些都是函数相关的堆栈指针,是有栈帧结构的,而且和之前的esp并不冲突,因为这些是编译器自动生成的代码,而且是保存到栈里面了。),然后跳转到b()
。因为处于任务A中,经过判断,无法调度到A。b()
做完该做的事情,栈撤除,返回。
注意:在这个过程中,由于并没有任务切换,故A任务上下文环境并没有变化。 -
接着执行
c();
,会自然地在栈中保存返回地址,形成自己的栈结构,因为会有如下的代码被执行:
pushl %ebp
movl %ebp,%ebp```
- 假如在执行的过程中,不幸时间片到了,被调度到B,A的任务上下文环境保存(**第二次保存A任务上下文**),但是已经被改变了(eip肯定指向了c()中将要执行的某一指令,esp,ebp由于栈帧的形成也变化了)。
- 恢复到B任务执行,B在执行的时候,`b()`会执行调度到A。
- 恢复为A任务上下文环境的时候,此时A中的esp位置由于之前调用`c()`的时候向下(x86栈的增长方式是是满减)移动了几个位置,***那么恢复的时候,无法找到我们之前(第一次A被调度出来,A中eip保存的是`b();`的地址)保存的上下文环境(因为sp,ip均被A中对`c();`的调用破坏掉)。***
***那么看看之前我们在堆栈中又保存了什么呢?***
>实际上,由于我们采用了任务控制块中特殊的变量sp,ip来保存esp,eip,那么实际上任务上下文环境并未保存堆栈中。
***再看看我们恢复的时候又用了堆栈里面的什么?***
>什么也没用到,还是只用到了tcb的特殊变量sp,ip。实际上,在任务切换的时候,堆栈仅仅被我们用来迂回地改变eip,进行函数跳转执行,而这些动作,对esp并无影响。
>这样一来,恢复到任务A的过程是没有问题的。但是要记住的是,恢复到的并不是第一次调度的地方,而是恢复到`c();`中被中断的地方。
- 恢复到A之后,因为`c();`还没有执行完成,所以,要等待`c();`执行完,当`c();`执行完成之后,`c();`用的栈撤除,返回。
- 至此A执行完毕,可见,在任务的栈sp,ip被函数的调用或者其他破坏掉的时候,也不会影响任务的正常执行流程。
*任务会有很多个调度点,但是这很多个调度点是按照时间的先后顺序来的,而且后面的调度点相对于前面的调度点也完成了很多有意义的事情,所以我们只需要恢复到最近的一个任务调度点就可以了,恢复到以前的任务调度点反而是错误的。*
*之所以这样,我是这样理解的,任务保存的esp、eip终归是为任务中的函数服务的,对于任务切换来说,并没有实际的意义—怎么变化都没关系,只要能找到将要执行的指令,并且栈地址无误,而这一点被编译器良好的栈的建立和撤除保证。*
在这个过程中,任务的切换并没有用到ebp,函数调用准确返回,任务也能完好的切换。
所以,个人觉得在任务切换中,并不需要保存和恢复ebp。
###===============UPDATE================
*神逆转来了。。。*
g:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
addl $3, %eax
popl %ebp
ret
f:
pushl %ebp
movl %esp, %ebp
subl $4, %esp
movl 8(%ebp), %eax
movl %eax, (%esp)
call g
leave
ret
main:
pushl %ebp
movl %esp, %ebp
subl $4, %esp
movl $8, (%esp)
call f
addl $1, %eax
leave
ret
千算万算,没有考虑到上面的这种情况,如果两个任务的入口都是上面的这个函数,如果在上下文切换的时候,不对ebp保存、恢复,那么会有如下分析:
>A任务执行完下面这几句(ebp内为值a),突然被调度到B,B任务也执行完下面这几句(ebp内为值b),然后被调度回A。此时值得注意的是,B任务已经更新了ebp内容,a肯定不等于b。
g:
pushl %ebp
movl %esp, %ebp
切换回去的时候,因为ebp的值已经错乱掉了,那么在`movl 8(%ebp), %eax`引用参数的时候,肯定会引用错误(可能非法访问不属于自己的内存)。
这种情况,可以自己写代码模拟下。
>总结下来,ebp的保存还是有必要的,只不过自己的理解不够深入,随着自己慢慢的挖掘,可能会发现,会有更多没想到的地方。
***--update于2017/3/8 22:45***
网友评论