`
spartan1
  • 浏览: 365139 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

ucore-project4: 内核态和用户态切换(1)

 
阅读更多

终于又回来了。内核态和用户态切换比较麻烦,今天解决了一个bug,搞定了从内核态切换到用户态。

 

x86中,从高优先级代码切换到低优先级代码的唯一方法就是使用ret或iret返回指令,而从低优先级切换到高优先级的方法是int或call调用指令。这几个指令在跨优先级过程中,都会进行堆栈切换。而切换的目的堆栈,对于int/call指令,是记录在当前task的tss段中,对于ret/iret指令,是记录在当前堆栈返回指令的底下。

 

先说明本次用的从内核态切换到用户态的处理方式。

 

本次是使用中断处理程序的iret指令完成内核态到用户态的切换。首先在内核态(cpl=0)执行int中断指令,因同样在内核态,不会造成堆栈切换,所以当前堆栈返回指令的底下也不会有切换堆栈的地址。为此构造一个新的trapframe结构,将trap_c函数的参数tf所指向的trapframe拷贝到新的trapframe,同时给其中的esp、ss赋值,esp的值就是正常返回后的地址,而ss的值是用户态段选择子(实际上和内核态空间完全重合,只不过优先级不同)。之后将新的trapframe的地址赋值给trap_c函数的参数tf:

temptf_k2u = *tf;
temptf_k2u.esp = (unsigned int)tf + sizeof(struct trapframe) - 8;
temptf_k2u.ss = SS_UDATA;
temptf_k2u.cs = SS_UTEXT;
temptf_k2u.ds = SS_UDATA;
temptf_k2u.es = SS_UDATA;
temptf_k2u.fs = SS_UDATA;
temptf_k2u.gs = SS_UDATA;

tf = &temptf_k2u;

 其中最后一句是重点。在C语言中,一般来说,函数的参数实际上就是函数的局部变量,在函数返回后被直接丢弃,所以给函数的参数赋值没有任何意义。实际上函数的参数和局部变量都放在堆栈中,其中函数参数在调用函数返回地址的下面,而函数的局部变量在调用函数返回地址的上面,所以执行完成ret后,栈指针esp是指向第一个参数的,一般来说都是直接将esp加上某个值,从而直接将所有参数丢弃,所以造成参数被直接丢弃的后果。

 

而在trap_asm中,并没有直接把参数丢弃,而是把参数值赋值给了esp(通过popl %esp指令),这样,参数实际上即作为trap_c函数的输入值,有作为trap_c函数的输出值来使用:

.globl trap_asm
trap_asm:
    pushl %gs
    pushl %fs
    pushl %es
    pushl %ds
    pushal

    pushl %esp
    call trap_c /* trap_c(taskframe *tf) */
    popl %esp

    popal
    popl %ds
    popl %es
    popl %fs
    popl %gs

    addl $8, %esp /* remove trap number and error code */
    iret

 其中第九行 pushl %esp将当前的esp压入堆栈,作为参数tf,调用trap_c完成后,第11行popl %esp将tf参数再次弹出来,作为esp的值。而在trap_c中,因为已经给tf赋了一个新值&temptf_k2u,所以后续的pop操作实际上都是在temptf_k2u上做的。从而后续一系列pop和iret,就完成了优先级从kernel到user态的切换。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics