操作系统实践之中断程序分析

%macro hwint_master 1
        call save
        in alINT_M_CTLMASK
        or al(1 << %1)
        out INT_M_CTLMASK, al
        mov al20h
        out 20h, al
        sti
        push %1
        call [irq_table+4*%1] ;调用真正的处理函数
        add esp4
        cli
        in alINT_M_CTLMASK
        and al~(1 << %1)
        out INT_M_CTLMASK, al
        ret
%endmacro


save:
        pushad
        push ds
        push es
        push fs
        push gs
        mov dxss
        mov dsdx
        mov esdx
        mov esiesp
        inc dword [k_reenter]
        cmp dword [k_reenter], 0
        jne .1
        mov espStackTop
        push restart
        jmp [esi RETADR P_STACKBASE]    
.1:
        push restart_reenter
        jmp [esi RETADR P_STACKBASE]

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

 

//时间中断的真正处理函数

PUBLIC void clock_handler(int irq)
{
    //disp_str("#");
    ticks++;
    p_proc_ready->ticks--;
    if(k_reenter!=0)
    {
     //disp_str("!");
     return;
    }
    if (p_proc_ready->ticks 0) {
 return;
   }
    schedule();
}

分析:Tinix操作系统目前的运行状态分为内核态(ring0)和任务态(ring1),ring1是进程代码,然后这些进程的内核对象即进程表是在ring0下访问的,进程表就是一个结构体数组,每个结构体保存了进程上次运行时各个寄存器的状态,包括gs,fs,es,ds,edi,esi,ebp,ebx,edx,ecx,eax,eip,cs,eflags,esp,ss,这些寄存器在tinix_main(运行在ring0下)中进行了初始化,其中eip指向进程的入口函数,即TestA,TestB,TestC,esp指向各自不同的栈空间,并且p_proc_ready = proc_table,p_proc_ready 即下一个将要运行的进程的内核对象,tinix_main最后会调用restart,restart会将下一个将要运行的进程的内核对象中的regs地址赋给tss的sp0,这样进行运行发生中断时,堆栈要切换到ring0,栈指针sp从tss的sp0读入,这样中断处理程序就可以刚好在内核对象中保存上述寄存器了(严格来说eip,cs,eflags,esp,ss是CPU自动压栈的),restart最后执行iretd指令就恢复进程的运行了。

   下面看一下中断处理的流程,进入中断处理程序后,中断屏蔽位是开着的,这是不会发生硬件中断,进入save后通过pushad、push es,push fs,push gs把之前的进入中断之间的寄存器状态保存起来,不过不是中断重入的话保存的位置就是进程内核对象,保存完以后需要将sp指向内核堆栈,以免以后的堆栈操作破坏进程结构;如果是中断重入,保存的位置就是内核栈的某个位置(中断重入的情况不会发生堆栈切换),从call save返回后,中断处理程序首先关闭掉了同类的中断,也就是说在时钟处理过程中是不会发生时钟中断的中断重入的,同理键盘中断的处理过程中叶不会发生键盘中断重入。然后激活EOI,开中断,这时候别的中断可以发生了。接下来调用  call [irq_table+4*%1]来进行实际的中断处理,接下来要关闭中断,通过操作EOI重新打开同类中断,ret指令后,如果是中断重入,程序执行到restart_reenter,直接重中断返回;如果不是中断重入,执行到restart,此时需要将堆栈切换到进程内核对象处,因为进入中断前的寄存器状态保存在内核对象处。通过iretd指令从中断返回后中断又重新打开。

  上述流程保证了中断处理函数在执行时同类的中断不会发生,我们来看看时钟中断处理函数clock_handler,只有不是中断重入的时候才会切换进程,否则什么都不做。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值