扩展proj4,增加syscall功能,即增加一用户态函数(可执行一特定的系统调用:获得始终计数值),当内核初始化完毕后,可从内核态返回到用户态的函数,而用户态的函数用通过系统调用得到内核态的服务
- 在操作系统中,内核态对应的是ring0,而用户态对应的是ring3,ring0和ring3指的是当前代码运行的特权等级。这个特权登记由cs中的标识特权级的几位进行声明。在访问数据段是,数据段的特权级(DPL)必须大于等于代码段的特权级(CPL)。
由于要求通过中断实现用户态到内核态的转变,需要知道T_SWITCH_TOU以及T_SWITCH_TOK的中断号。在trap,h中存在定义
#define T_SWITCH_TOU
#define T_SWITCH_TOK
查看中断处理函数入口vector.S,可以看到中断处理函数为trapentry.S中的_alltraps函数,而__alltraps就是将各种寄存器压栈保存,然后调用trap.c中的trap函数,最后恢复各寄存器并返回,在trap.h中可发现压栈保存的数据结构
struct pushregs {
uint32_t reg_edi;
uint32_t reg_esi;
uint32_t reg_ebp;
uint32_t reg_oesp;
uint32_t reg_ebx;
uint32_t reg_edx;
uint32_t reg_ecx;
uint32_t reg_eax;
};
由上述可知从内核态切换到用户态只能通过iret指令修改cs的值。也就是修改之前压入栈中的值,iret指令将栈中的值弹出时修改cs的值。同时为了之后的访问不出问题,需要将ds,es,ss中的特权级一并修改,并且要修改i/o特权级。
下述使用内联汇编。在调用中断之前需要先修改esp,因为当切换优先级时,iret指令会额外弹出ss和esp,但调用中断时并不会产生特权级的切换,因此不用压入ss和esp。需要预先留出空间防止代码出错。从用户,态切换到内核态时,由于用户态调用中断时本来就自动切换到内核态,因此不会产生上述的问题,无需额外压入ss和esp
对于中断内部的处理代码需要对trap.c中的trap_dispatch的switch里面增加部分代码用于返回ticks。
trap_dispatch的switch代码如下:
在lab1中使用make qemu运行结果如下: