读书人

ucore-project4: 内核态跟用户态切换(

发布时间: 2012-07-18 12:05:40 作者: rapoo

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

这是内核态和用户态切换的最后一篇,也是project4的最后一篇了。

?

完成了使用call gate进行用户态到内核态的切换,使用lret进行内核态到用户态的切换的实验。这是project4.1.2的内容。

?

内核态到用户态的切换比较麻烦一些,首先将用户态段selector给ds,es,fs,gs赋值,将用户态栈的selector压栈,然后ebp压栈。因为c编译器会再函数开头将ebp压栈,然后将esp赋值给ebp,因此压栈的实际上就是压用户态栈selector前的esp的值。然后将用户态代码段selector压栈,然后将lret下一条指令位置压栈,然后执行lret,完成。代码如下:

static void callk2u(){    asm volatile ("movw %0, %%ax;"                         "movw %%ax, %%ds;"                         "movw %%ax, %%es;"                         "movw %%ax, %%fs;"                         "movw %%ax, %%gs;"                         "pushl %0;"                         "pushl %%ebp;"                         "pushl %1;"                         "pushl $1f;"                         "lret;"                         "1:"                         ::"i"(SS_UDATA), "i"(SS_UTEXT));}

?callk2u函数返回后,cpu已经处于用户态了。

用户态切换到内核态代码比较简单,使用callgate操作,不过在这里犯了个错误,调用lcall时,没有传入callgate的selector(51),而是传了callgate在gdt数组中的下标(6),导致调用出错,改过来就好了。

首先建立callgate:

setcallgate(&gdt[GATE_CALLU2K], SS_KTEXT, to_kernel, 0, DPL_USER);

?

这里to_kernel函数用来做假,将栈换掉,执行ret而不是lret,所以to_kernel返回时,不是原样不变返回原来的用户态,而是处于内核态了,并且代码还继续往后执行。to_kernel代码如下:

static void to_kernel(){    asm volatile("movw %0, %%ax"                        "movw %%ax, %%ds;"                        "movw %%ax, %%es;"                        "movw %%ax, %%fs;"                        "movw %%ax, %%gs;"                        "movl 4(%%ebp), %%eax;"                        "movl 12(%%ebp), %%esp;"                        "pushl %%eax;"                        "pushl %%ebp;"                        ::"i" (SS_KDATA));}

?

其中最后一句pushl %%ebp主要是用来应对c编译器为to_kernel生成的popl %ebp; ret这两条指令的,也可以不要pushl %%ebp,直接使用ret指令,此时编译器自动生成的popl %ebp; ret两条指令就不会被执行了。无论哪种方式,都不会对堆栈产生影响。

?

调用callgate的代码如下(因使用lcall,所以也要使用汇编来处理):

static void callu2k(){    asm volatile ("lcall %0, $0;"                         ::(i)(GS_CALLU2K));}

?

读书人网 >操作系统

热点推荐