全面剖析《自己动手写操作系统》第六章--中断处理程序

时间:2022-03-18 14:36:21

转载请标注:http://blog.csdn.net/zgh1988/article/details/7389329

下面我将分别以c和d为例,来讲述单进程切换和多进程切换下的中断处理程序

1、单进程环境下的中断处理程序

2、多进程环境下的中断处理程序

一、单进程环境下的中断处理程序

    在这里我们只考虑利用时钟中断来进行进程切换。

    我们知道进程是运行在ring1环境下的,而中断处理程序是运行在ring0环境下的,让我们想象一下进程切换时的情形。一个进程正在兢兢业业地运行着,这时候时钟中断发生了,特权级从ring1跳到ring0,开始执行时钟中断处理程序,中断处理程序这时调用进程调度模块,指定下一个应用运行的进程,当中断处理程序结束时,下一个进程准备就绪并开始运行,特权级又从ring0跳回到ring1。

    下面我们从两个方面来考虑进程切换过程中需要注意什么?(假设此时运行的是进程A)

1、现场的保存和恢复

    进入中断处理程序之后,首要任务就是保存进程A的状态信息,以便于将来恢复进程A时使用。在本程序中,进程A的状态信息只包含寄存器内容。由于是单进程环境下的中断,所以在离开中断处理程序之前,要恢复进程A的状态信息,离开中断处理程序之后,就又返回到进程A。

hwint00:
……
;保存原寄存器的内容
pushad
push ds
push es
push fs
push gs
………

;恢复原寄存器的内容
pop gs
pop fs
pop es
pop ds
popad

……
iretd

2、堆栈的切换

    中断发生,特权级发生了变化,从ring1(进程A)----- ring0(中断处理程序)-----ring1(进程A)。

    首先考虑ring1----ring0,从低特权级(ring1)跳转到高特权级(ring0),需要从TSS中获取es0和esp0。进入到ring0(中断处理程序)之后,我们需要将进程A的状态信息保存到PCB(进程控制块)中,所以在发生中断处理程序之前,我们将TSS中esp0指向进程表。所以在restart函数中,我们看到:

restart:
movesp, [p_proc_ready];将进程表首地址赋值给esp
lldt[esp + P_LDT_SEL];加载PCB中的LDT
leaeax, [esp + P_STACKTOP];将eax指向PCB中的eax的地址
movdword [tss + TSS3_S_SP0], eax;将TSS中的esp0指向PCB中的eax的地址

;此时esp指向进程表首地址,连续出栈,依次弹出gs, fs, es, ds, edi
;esi, ebp, esp, ebx, edx, ecx, eax。
popgs
popfs
popes
popds
popad

;此时esp指向eip
addesp, 4
iretd

全面剖析《自己动手写操作系统》第六章--中断处理程序

结合图形来分析程序。

    我们知道,我们将esp0指向了数据结构PROCESS中STACK_FRAME的eax,所以在时钟中断发后,进入中断处理程序,首先需要保存进程状态信息,即通过一堆push指令来完成,刚好保存到进程的PCB中。也就是PROCESS结构。

hwint00:; Interrupt routine for irq 0 (the clock).
sub esp, 4

;保存原寄存器的内容
pushad
push ds
push es
push fs
push gs

mov dx, ss
mov ds, dx
mov es, dx

;改变屏幕第0行,第0列的字符
;inc byte[gs:0]

;使主8259接受中断
mov al, EOI
out INT_M_CTL, al

inc dword[k_reenter]
cmp dword[k_reenter], 0
jne .re_enter

;切换到内核栈
mov esp, StackTop

;打开中断
sti

;调用disp_str函数,用来显示“^”
push clock_int_msg
call disp_str
add esp, 4

;调用delay函数,实现中断嵌套
;push 1
;call delay
;add esp, 4

;关闭中断
cli

;离开内核栈
mov esp, [p_proc_ready]

lea eax, [esp + P_STACKTOP]
mov dword[tss + TSS3_S_SP0], eax

.re_enter:
dec dword[k_reenter]

;恢复原寄存器的内容
pop gs
pop fs
pop es
pop ds
popad

add esp, 4

iretd

    在ring0中,我们需要调用进程调度模块,或者显示某些字符,需要使用堆栈,此时的esp指向的是进程表,所以如果对esp进行操作,相当于改变了进程表的内容。所以我们此时需要将esp保存起来,然后将其指向另一块堆栈(即内核栈)即:

mov esp, StackTop。

    执行完调度模块,或者显示某些字符之后,我们需要离开内核栈,那么此时我们需要将esp指向何处呢?我们知道,在离开中断处理程序之前,我们需要恢复进程A的状态信息,所以要通过一堆pop指令来完成。于是我们必须将esp赋值为指向进程表的起始地址。即:mov esp, [p_proc_ready]

    这时候,我们还需要考虑一个问题,就是我们在离开中断处理程序之前,应该还要完成一个任务,就是赋值TSS中的esp0。所以我们使用

mov esp, [p_proc_ready]

lea eax, [esp + P_STACK_TOP]

mov dword[tss + TSS3_S_SP0], eax

这段代码,与restart中赋值esp0的一模一样。

此时我们完成了ring0 --- ring1的跳转。

二、多进程环境下的中断处理程序

    在多进程切换环境下,要求多个进程进行切换,所以每次在发生中断处理程序之后,我们需要从进程表中取出下一个等待的进程,然后去执行该进程。所以,在多进程环境下的中断处理程序只比之前多了一个任务,那就是在离开中断处理程序之前,将一个等待的进程取出。    下面是程序代码:
hwint00:; Interrupt routine for irq 0 (the clock).
sub esp, 4

;保存原寄存器的内容
pushad
push ds
push es
push fs
push gs

mov dx, ss
mov ds, dx
mov es, dx

;使主8259接受中断
mov al, EOI
out INT_M_CTL, al

inc dword[k_reenter]
cmp dword[k_reenter], 0
jne .re_enter

;切换到内核栈
mov esp, StackTop

;打开中断
sti

;调用clock_handler函数,通过调整p_proc_ready的指向proc_table
;数组中不同的进程。
push 0
call clock_handler
add esp, 4

;关闭中断
cli

;离开内核栈
mov esp, [p_proc_ready]
lldt [esp + P_LDT_SEL]
lea eax, [esp + P_STACKTOP]
mov dword[tss + TSS3_S_SP0], eax

.re_enter:
dec dword[k_reenter]

;恢复原寄存器的内容
pop gs
pop fs
pop es
pop ds
popad

add esp, 4

iretd

我们使用调度模块,来完成了进程的调度,该进程调度简单,就是依次运行进程表中的每一个进程。我们可以查看clock_handler函数。它是这样完成的:
PUBLIC void clock_handler(int irq)
{
disp_str("#");
p_proc_ready++;
if (p_proc_ready >= proc_table + NR_TASKS)
{
p_proc_ready = proc_table;
}
}

这样之后,我们就完成了不同进程之间的切换。

全面剖析《自己动手写操作系统》第六章--进程  http://blog.csdn.net/zgh1988/article/details/7371754

全面剖析《自己动手写操作系统》第五章--makefile http://blog.csdn.net/zgh1988/article/details/7338380

全面剖析《自己动手写操作系统》第五章---加载内核kernel.bin http://blog.csdn.net/zgh1988/article/details/7329941

全面剖析《自己动手写操作系统》第五章---Red Hat 9.0 的安装过程  http://blog.csdn.net/zgh1988/article/details/7315676

全面剖析《自己动手写操作系统》第四章---FAT12文件系统 http://blog.csdn.net/zgh1988/article/details/7284834

全面剖析《自己动手写操作系统》第四章---加载Loader.bin http://blog.csdn.net/zgh1988/article/details/7291909

全面剖析《自己动手写操作系统》第三章---进入保护模式   http://blog.csdn.net/zgh1988/article/details/7098981

全面剖析《自己动手写操作系统》第三章---“实模式--保护模式--实模式” http://blog.csdn.net/zgh1988/article/details/7255804

全面剖析《自己动手写操作系统》第三章---堆栈段的工作方式 http://blog.csdn.net/zgh1988/article/details/7256254

全面剖析《自己动手写操作系统》第三章---特权级以及不同特权级代码段之间的跳转 http://blog.csdn.net/zgh1988/article/details/7262901

全面剖析《自己动手写操作系统》第三章---分页机制 http://blog.csdn.net/zgh1988/article/details/7270748

全面剖析《自己动手写操作系统》第三章---中断机制 http://blog.csdn.net/zgh1988/article/details/7276259

全面剖析《自己动手写操作系统》第二章http://blog.csdn.net/zgh1988/article/details/7062065

全面剖析《自己动手写操作系统》第一章http://blog.csdn.net/zgh1988/article/details/7060032

《自己动手写操作系统》读后感http://blog.csdn.net/zgh1988/article/details/7059936