zoukankan      html  css  js  c++  java
  • 操作系统开发系列—13.b.进程之丰富中断处理程序

    首先打开时钟中断:

    	out_byte(INT_M_CTLMASK,	0xFE);	// Master 8259, OCW1.
    	out_byte(INT_S_CTLMASK,	0xFF);	// Slave  8259, OCW1.
    

    为了让时钟中断可以不停地发生而不是只发生一次,还需要设置EOI:

    hwint00:		; Interrupt routine for irq 0 (the clock).
    	mov	al, EOI		; `. reenable
    	out	INT_M_CTL, al	; / master 8259
    	iretd
    

    运行后发现结果和原来没有任何区别,因为我们只是可以继续接受中断而已,其余并没有做什么。

    中断现在已经被打开,于是就存在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
    
    	inc	byte [gs:0]	; 改变屏幕第 0 行, 第 0 列的字符
    
    	mov	al, EOI		; `. reenable
    	out	INT_M_CTL, al	; /  master 8259
    
    	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
    

    你可以看到,sub/add esp这两句代码实际上是跳过了4字节,结合进程表的定义知道,被跳过的这4字节实际上就是那个retaddr,我们还是先不管这个值。

    我们曾经提到过内核栈的问题,如今这个问题真的出现了。现在esp指向的是进程表,如果此时我们要执行复杂的进程调度程序呢?最简单的例子是如果我们想调用一个函数,这时一定会用到堆栈操作,那么我们的进程表立刻会被破坏掉。所以我们需要切换堆栈,将esp指向另外的位置。

    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
    	
    	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
    

    切到内核栈和重新将esp切到进程表都很简单,一个mov语句就够了,但是它却非常关键。如果没有这个简单的mov,随着中断例程越来越大,出错的时候,你可能都不知道错在哪里。

    在这里我们尽可能地把代码放在使用内核栈的过程中来执行,只留下跳回进程所必需的代码。我们在这里试一下,把这段打印字符的代码替换成使用DispStr这个函数:

    extern	disp_str
    
    [SECTION .data]
    clock_int_msg		db	"^", 0
    
    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
    	
    	push	clock_int_msg
    	call	disp_str
    	add	esp, 4
    	
    	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
    

    运行结果如下,我们看到不断出现的字符“^”,说明函数disp_str运行正常,而且没有影响到中断处理的其他部分以及进程A,之所以在两次字符A的打印中间有多个“^”,是因为我们的进程执行体中加入了delay()函数,在此函数的执行过程中发生了多次中断:

    源码

  • 相关阅读:
    第19篇-Kibana对Elasticsearch的实用介绍
    第18篇-用ElasticSearch索引MongoDB,一个简单的自动完成索引项目
    第17篇-使用Python的初学者Elasticsearch教程
    第16篇-关于Elasticsearch的6件不太明显的事情
    第15篇-使用Django进行ElasticSearch的简单方法
    第14篇-Python中的Elasticsearch入门
    第13篇-Elasticsearch查询-术语级查询
    第12篇-Elasticsearch全文查询
    MQTT
    rest-framework-@action()装饰器
  • 原文地址:https://www.cnblogs.com/joey-hua/p/5468155.html
Copyright © 2011-2022 走看看