操作系统开发系列—13.c.进程之中断重入

现在又出现了另外一个的问题,在中断处理过程中是否应该允许下一个中断发生?

让我们修改一下代码,以便让系统可以在时钟中断的处理过程中接受下一个时钟中断。这听起来不是个很好的主意,但是可以借此来做个试验。

首先,因为CPU在响应中断的过程中会自动关闭中断,我们需要人为地打开中断,加入sti指令;然后,为保证中断处理过程足够长,以至于在它完成之前就会有下一个中断产生,我们在中断处理例程中调用一个延迟函数。代码如下:

extern	delay

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
	
	mov	esp, StackTop		; 切到内核栈

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

	mov	al, EOI			; `. reenable
	out	INT_M_CTL, al		; /  master 8259

	sti
	
	push	clock_int_msg
	call	disp_str
	add	esp, 4

	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

	pop	gs	; `.
	pop	fs	;  |
	pop	es	;  | 恢复原寄存器值
	pop	ds	;  |
	popad		; /
	add	esp, 4

	iretd

运行如下,在打印了一个A0x0之后就不停打印“^”,再也进不到进程里面:

之所以会产生这种情况,是因为在一次中断还未处理完时,又一次中断发生了。这时程序又跳到中断处理程序的开头,如此反复,永远也执行不到中断处理程序的结尾——跳回进程继续执行。

这个问题并不难解决,只要设置一个全局变量就可以了。这个全局变量有一个初值-1,当中断处理程序开始执行时它自加,结束时自减。在处理程序开头处这个变量需要被检查一下,如果值不是0(0=-1+1),则说明在一次中断未处理完之前就又发生了一次中断,这时直接跳到最后,结束中断处理程序的执行。当然,武断地结束新的中断并不是一个好的办法,这里我们姑且先这样来做。

PUBLIC int kernel_main()
{
...
	k_reenter = -1;
...
}

然后在中断例程中加入k_reenter自加以及判断是否为0的代码:

extern	k_reenter

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

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

	mov	al, EOI			; `. reenable
	out	INT_M_CTL, al		; /  master 8259

	inc	dword [k_reenter]
	cmp	dword [k_reenter], 0
	jne	.re_enter
	
	mov	esp, StackTop		; 切到内核栈

	sti
	
	push	clock_int_msg
	call	disp_str
	add	esp, 4

	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:	; 如果(k_reenter != 0),会跳转到这里
	dec	dword [k_reenter]
	pop	gs	; `.
	pop	fs	;  |
	pop	es	;  | 恢复原寄存器值
	pop	ds	;  |
	popad		; /
	add	esp, 4

	iretd

运行如下,可以看到,字符A和相应的数字又在不停出现了,这说明我们的修改生效了,而且,屏幕左上角的字母跳动速度不变还是以前一样快而字符“^”打印速度变慢许多,说明有很多时候程序在执行了inc byte [gs:0]之后并没有执行disp_str,这也说明中断重入的确发生了:

好了,我们已经有了一个办法来解决中断重入这个问题,那么注释掉刚才的打印字符以及Delay等语句:

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

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

	mov	al, EOI			; `. reenable
	out	INT_M_CTL, al		; /  master 8259

	inc	dword [k_reenter]
	cmp	dword [k_reenter], 0
	jne	.re_enter
	
	mov	esp, StackTop		; 切到内核栈

	sti
	
	push	clock_int_msg
	call	disp_str
	add	esp, 4

;;; 	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:	; 如果(k_reenter != 0),会跳转到这里
	dec	dword [k_reenter]
	pop	gs	; `.
	pop	fs	;  |
	pop	es	;  | 恢复原寄存器值
	pop	ds	;  |
	popad		; /
	add	esp, 4

	iretd

再次运行,字符“^”打印的速度又变快了,

 

源码

posted @ 2016-05-08 17:21  是非猫  阅读(1158)  评论(0编辑  收藏  举报