• 操作系统开发系列—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
    

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

    源码

  • 相关阅读:
    go引入包一直是红色,没有引入的解决办法
    php 把抛出错误记录到日志中
    亚马逊查询接口
    git 合并指定文件到另一个分支
    content-type
    Echarts(饼图Pie)
    DIN 模型速记
    DeepFM 要点速记
    youtube DNN 模型要点速记
    java设计模式之迭代器
  • 原文地址:https://www.cnblogs.com/joey-hua/p/5468634.html
Copyright © 2020-2023  润新知