《自己动手写操作系统》多进程代码分析

  主要的代码有三部分:(在chapter6\k\kernel中)



  (1)kernel.asm中时钟中断处理的部分:

  

; 中断和异常 -- 硬件中断
; ---------------------------------
%macro	hwint_master	1
	call	save
	in	al, INT_M_CTLMASK	; `.
	or	al, (1 << %1)		;  | 屏蔽当前中断
	out	INT_M_CTLMASK, al	; /
	mov	al, EOI			; `. 置EOI位
	out	INT_M_CTL, al		; /
	sti	; CPU在响应中断的过程中会自动关中断,这句之后就允许响应新的中断
	push	%1			; `.
	call	[irq_table + 4 * %1]	;  | 中断处理程序
	pop	ecx			; /
	cli
	in	al, INT_M_CTLMASK	; `.
	and	al, ~(1 << %1)		;  | 恢复接受当前中断
	out	INT_M_CTLMASK, al	; /
	ret
%endmacro


ALIGN	16
hwint00:		; Interrupt routine for irq 0 (the clock).
	hwint_master	
其中的save是
; ====================================================================================
;                                   save
; ====================================================================================
save:
        pushad          ; `.
        push    ds      ;  |
        push    es      ;  | 保存原寄存器值
        push    fs      ;  |
        push    gs      ; /
        mov     dx, ss
        mov     ds, dx
        mov     es, dx

        mov     eax, esp                    ;eax = 进程表起始地址

        inc     dword [k_reenter]           ;k_reenter++;
        cmp     dword [k_reenter], 0        ;if(k_reenter ==0)
        jne     .1                          ;{
        mov     esp, StackTop               ;  mov esp, StackTop <--切换到内核栈
        push    restart                     ;  push restart
        jmp     [eax + RETADR - P_STACKBASE];  return;
.1:                                         ;} else { 已经在内核栈,不需要再切换
        push    restart_reenter             ;  push restart_reenter
        jmp     [eax + RETADR - P_STACKBASE];  return;
                                            ;}
最后ret跳到的地方是

; ====================================================================================
;                                   restart
; ====================================================================================
restart:
	mov	esp, [p_proc_ready]
	lldt	[esp + P_LDT_SEL] 
	lea	eax, [esp + P_STACKTOP]
	mov	dword [tss + TSS3_S_SP0], eax
restart_reenter:
	dec	dword [k_reenter]
	pop	gs
	pop	fs
	pop	es
	pop	ds
	popad
	add	esp, 4
	iret


  (2)clock.c中时钟中断中调用的进程调度函数:

/*======================================================================*
                           clock_handler
 *======================================================================*/
PUBLIC void clock_handler(int irq)
{
	disp_str("#");

	if (k_reenter != 0) {
		disp_str("!");
		return;
	}

	p_proc_ready++;
	if (p_proc_ready >= proc_table + NR_TASKS) {
		p_proc_ready = proc_table;
	}



  (3)main.c中三个进程的执行函数:

/*======================================================================*
                               TestA
 *======================================================================*/
void TestA()
{
	int i = 0;
	while(1){
		disp_str("A");
		disp_int(i++);
		disp_str(".");
		delay(1);
	}
}

/*======================================================================*
                               TestB
 *======================================================================*/
void TestB()
{
	int i = 0x1000;
	while(1){
		disp_str("B");
		disp_int(i++);
		disp_str(".");
		delay(1);
	}
}

/*======================================================================*
                               TestB
 *======================================================================*/
void TestC()
{
	int i = 0x2000;
	while(1){
		disp_str("C");
		disp_int(i++);
		disp_str(".");
		delay(1);
	}
}



最后执行的结果是




  可以看到,执行结果中出现了连续的#,但是从来没有出现过!(!会在中断重入的时候显示),原因是:

  从call save 到 ret 的这段时间内,时钟中断一直是关闭的(save执行完之后通过写ocw1屏蔽了时钟中断),置eoi并执行sti后,开启的是对其它中断的响应,对当前中断的响应是在ret之前才开启的。

  因此这段时间里如果发生了时钟中断是不会重入的,其它的中断可以重入,这期间如果发生时钟中断会在本次中断完成后iretd后立即处理(iretd会置if位,iretd之前恢复了对本中断即时钟中断的屏蔽,前面也写过了eoi,此三个处理缺少任意一个都不能使时钟中断开启),因此在这种情况下会出现##;而如果在进程执行过程中出现中断(delay过程中很容易出现),这时会出现如A0x3.#。

  这里涉及到了关闭中断过程中的中断如何处理,在关闭中断期间有新的中断到达,新中断是会在当前中断结束后立即响应的,我所查的资料的原文是这样的:

Disabled interrupts are not lost; the PIC sends them to the CPU as soon as they are 
enabled again. 

  


  那么我们如何才能使时钟中断本身可以发生嵌套呢,我们可以将kernel.asm中屏蔽当前中断的那段代码注掉,即:

; 中断和异常 -- 硬件中断
; ---------------------------------
%macro	hwint_master	1
	call	save
	;in	al, INT_M_CTLMASK	; `.
	;or	al, (1 << %1)		;  | 屏蔽当前中断
	;out	INT_M_CTLMASK, al	; /
	mov	al, EOI			; `. 置EOI位
	out	INT_M_CTL, al		; /
	sti	; CPU在响应中断的过程中会自动关中断,这句之后就允许响应新的中断
	push	%1			; `.
	call	[irq_table + 4 * %1]	;  | 中断处理程序
	pop	ecx			; /
	cli
	;in	al, INT_M_CTLMASK	; `.
	;and	al, ~(1 << %1)		;  | 恢复接受当前中断
	;out	INT_M_CTLMASK, al	; /
	ret
%endmacro

  此时,在置eoi位并执行sti置if位后,在中断处理程序中便能嵌套时钟中断了,为了保证在处理程序中可以发生时钟中断,我们在clock.c的处理过程中加一个delay(1)使其执行足够长的时间,即

/*======================================================================*
                           clock_handler
 *======================================================================*/
PUBLIC void clock_handler(int irq)
{
	disp_str("#");

	if (k_reenter != 0) {
		disp_str("!");
		return;
	}
        delay(1);

	p_proc_ready++;
	if (p_proc_ready >= proc_table + NR_TASKS) {
		p_proc_ready = proc_table;
	}

下面是执行的结果:



  可以看到,!即中断重入出现了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值