操作系统 您所在的位置:网站首页 什么是用户态什么是内核态 操作系统

操作系统

2023-03-22 00:45| 来源: 网络整理| 查看: 265

1 前言

        本文并非介绍当前使用的linux操作系统的用户态和内核态的切换细节,而是介绍ucore这个教学性质的系统,主要是为了说明用户态和内核态本质上到底是什么。

2 初始状态

        操作系统在加电启动、完成各个部分的初始化之后,处于内核态,假设当前的栈顶指针寄存器的值为x,指向内存的某个位置,如图2.1所示。

 3 内核态切换到用户态 #define T_SWITCH_TOU 120 // user/kernel switchstatic void lab1_switch_to_user(void) {//LAB1 CHALLENGE 1 : TODOasm volatile ("sub $0x8, %%esp \n""int %0 \n""movl %%ebp, %%esp": : "i"(T_SWITCH_TOU)); }

         (1)让esp寄存器的值减8,此时esp = x-8 。

        (2)执行软中断指令 int 120,调用120号中断处理程序。

                中断处理程序依次会往栈中压入以下寄存器值:eflags(32bit)、cs(16bit)、eip(32bit),压入之后,esp = x-20 ,栈变成如下形态。

 

 

         (3)120号中断处理程序,往栈中压入0和120(中断号),压入后,esp = x-28 。

.globl vector120 vector120:pushl $0pushl $120jmp __alltraps

 

        (4)跳转到 __alltraps 执行。

.globl __alltraps __alltraps:# push registers to build a trap frame# therefore make the stack look like a struct trapframepushl %dspushl %espushl %fspushl %gspushal# load GD_KDATA into %ds and %es to set up data segments for kernelmovl $GD_KDATA, %eaxmovw %ax, %dsmovw %ax, %es# push %esp to pass a pointer to the trapframe as an argument to trap()pushl %esp# call trap(tf), where tf=%espcall trap# pop the pushed stack pointerpopl %esp# return falls through to trapret... .globl __trapret __trapret:# restore registers from stackpopal# restore %ds, %es, %fs and %gspopl %gspopl %fspopl %espopl %ds# get rid of the trap number and error codeaddl $0x8, %espiret

         a)用32位对齐(pushl)的方式将一系列数据段寄存器夺入栈中。

        b)pushal指令也会压入一系列寄存器,包括:edi、esi、ebp、oesp、ebx、edx、ecx、eax。由于本文要分析的内容与这些寄存器没有关系,因而这些东西统称为pushregs。

        c)设置ds、es寄存器,指向内核数据段描述符。

        d)将当前的esp值压栈,到此,栈的形态如图所求。

 

        (5)“call trap”调用C语言的tap函数,而trap函数直接调用了trap_dispatch函数,我们假设直接call dispatch。

static void trap_dispatch(struct trapframe *tf) {case T_SWITCH_TOU:if (tf->tf_cs != USER_CS) {switchk2u = *tf;switchk2u.tf_cs = USER_CS;switchk2u.tf_ds = switchk2u.tf_es = switchk2u.tf_ss = USER_DS;switchk2u.tf_esp = (uint32_t)tf + sizeof(struct trapframe) - 8;// set eflags, make sure ucore can use io under user mode.// if CPL > IOPL, then cpu will generate a general protection.switchk2u.tf_eflags |= FL_IOPL_MASK;// set temporary stack// then iret will jump to the right stack*((uint32_t *)tf - 1) = (uint32_t)&switchk2u;}break; }

        a)call指令会把当前指令的下一条指令的地址eip+4压栈, 然后跳转到函数trap_dispatch代码执行。

        b)函数的第一第二条代码是把当前ebp压栈,然后设置ebp寄存器为当前esp寄存器的值,此时栈形态如图所示

pushl %ebp movl %esp, %ebp

 

 

        c)可以看到:ebp-8就是函数 trap_dispatch第一个参数的值(x-76),而x-76正好指向pushregs这一段数据,由x-76往上一段长为size_of(struct trapframe)的内存块刚好可以构成一个struct trapframe对象,这个对象刚好处于x-76到x这一段内存中!!

        (6)trap_dispatch代码

        a)首先找一个全局的变量(struct trapframe switchk2u ),在内存的某个地方,把参数tf指向的内容保存到switchk2u。

        b)把switchk2u中保存的cs寄存器的值改为用户态代码段的段选择子。

        c)把switchk2u中保存的ds、es、ss等寄存器的值改为用户态数据段的段选择子。

        d)把switchk2u中,在一开始预留的两个32位内存块的第二块设置为 x - 8 。

        e)设置switchk2u中保存的eflags寄存器的值,设置标志位 FL_IOPL_MASK 。

        f)设置x-80位置的值为switchk2u对象的地址。

 

 

        (7)trap_dispatch函数执行结束

leave ; 恢复栈帧指针ret ; 返回到main

        a)恢复栈帧指针:esp = ebp, popl %ebp。

        b)返回:popl %eip

 

         (8)返回(4)中的代码执行后续指令

popl %esp# return falls through to trapret... .globl __trapret __trapret:# restore registers from stackpopal# restore %ds, %es, %fs and %gspopl %gspopl %fspopl %espopl %ds# get rid of the trap number and error codeaddl $0x8, %espiret

        a)弹出栈顶值作为写入esp寄存器,此时esp=y 

 

 

        b)弹出pushregs这8个寄存器;

        c)弹出gs、fs、es和ds数据段寄存器;

        d)让esp = esp+8 ,至此,栈形态如图所示。

 

        e)iret指令弹出eip、cs和eflags;

        f)由于iret返回变更了eflags的特权级,因此还会弹出ss和sp。此时esp=x-8 。

 

4 总结

        用户态和内核态的切换本质上是eflags和cs、ds、es和ss等段寄存器值的变换。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有