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

转载请标注: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:
	mov	esp, [p_proc_ready]		;将进程表首地址赋值给esp
	lldt	[esp + P_LDT_SEL]		;加载PCB中的LDT
	lea	eax, [esp + P_STACKTOP]		;将eax指向PCB中的eax的地址
	mov	dword [tss + TSS3_S_SP0], eax	;将TSS中的esp0指向PCB中的eax的地址
	
	;此时esp指向进程表首地址,连续出栈,依次弹出gs, fs, es, ds, edi
	;esi, ebp, esp, ebx, edx, ecx, eax。
	pop	gs
	pop	fs			
	pop	es
	pop	ds
	popad

 ;此时esp指向eip
	add	esp, 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

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值