好了,完成了上面的工作,下一步的工作的什么呢,MINIX还把当前进程的寄存器组压栈的工作,以及判断是否发生中断重入的工作,还有切换内核栈的工作都放到了一个函数当中,并且在时钟中断处理程序的开始处调用了这个函数,那么必定在当前进程的PCB中压入的调用函数指令的下一条指令的相对偏移地址,那么PCB中又多了一个字段,就是这个相对偏移地址,那么我们就修改一下PCB的定义:
- typedef struct s_stack_frame
- {
- u32 gs;
- u32 fs;
- u32 es;
- u32 ds;
- u32 edi;
- u32 esi;
- u32 ebp;
- u32 kernel_esp;
- u32 ebx;
- u32 edx;
- u32 ecx;
- u32 eax;
- u32 retaddr;
- u32 eip;
- u32 cs;
- u32 eflags;
- u32 esp;
- u32 ss;
- }Stack_Frame;
那么下面就来修改时钟中断处理程序:
HW_Int_00:
call Save
mov al,20h
out 20h,al
sti
mov eax,0dh
push eax
push Int_Msg
call Disp_Color_Str
add esp,8
call Clock_Handler
cli
ret
...
Save:
pushad
push ds
push es
push fs
push gs
mov dx,ss
mov ds,dx
mov es,dx
mov eax,esp
inc dword [Is_Reenter]
cmp dword [Is_Reenter],0
jne .1
mov esp,Top_Of_Stack
push restart
jmp [eax + 48]
.1:
push reenter
jmp [eax + 48]
我们看到,添加了一个Save函数,一般的函数最后都会有个ret指令跳回到调用处的下一条指令处继续执行。但是这里用的是jmp,这是由于返回地址在PCB中保存的,而在时钟中断处理程序中切换到了内核栈,ret找不到返回地址,况且如果发生中断重入的话,返回地址保存在内核栈中。那就很混乱了,所以我们要以一种统一的方法来解决,先把esp送eax,再由eax找到返回地址,最后直接跳转过去。
编译运行,哪。。哪尼,出错了,哦哦,原来是在PCB中新添加的那个字段搞的鬼,修改一下:
restart:
mov esp,[p_Resume_PCB]
lea dword eax,[esp + 72]
mov [tss + 4],eax
mov ax,[esp + 72]
lldt ax
reenter:
dec dword [Is_Reenter]
pop gs
pop fs
pop es
pop ds
popad
add esp,4
iretd
好了,搞定了Save函数,让我们再来进一步修改中断处理程序。我们不想让时钟中断打断时钟中断,就是要去开中断之前屏蔽掉时钟中断,关中断之后开启时钟中断,来修改一下:
HW_Int_00:
call Save
mov al,20h
out 20h,al
in al,21h
or al,1
out 21h,al
sti
mov eax,0dh
push eax
push Int_Msg
call Disp_Color_Str
add esp,8
call Clock_Handler
cli
in al,21h
and al,0feh
out 21h,al
ret
编译运行,依旧的画面。