美文网首页Linux内核分析
mykernel-调度上下文ebp作用

mykernel-调度上下文ebp作用

作者: athorn | 来源:发表于2017-03-05 22:45 被阅读103次

    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***

    相关文章

      网友评论

        本文标题:mykernel-调度上下文ebp作用

        本文链接:https://www.haomeiwen.com/subject/tnsegttx.html