zoukankan      html  css  js  c++  java
  • 操作系统开发系列—12.f.在内核中添加中断处理 ●

    因为CPU只有一个,同一时刻要么是客户进程在运行,要么是操作系统在运行,如果实现进程,需要一种控制权转换机制,这种机制便是中断。

    要做的工作有两项:设置8259A和建立IDT。

    /*======================================================================*
                                init_8259A
     *======================================================================*/
    PUBLIC void init_8259A()
    {
    	/* Master 8259, ICW1. */
    	out_byte(INT_M_CTL,	0x11);
    	/* Slave  8259, ICW1. */
    	out_byte(INT_S_CTL,	0x11);
    	/* Master 8259, ICW2. 设置 '主8259' 的中断入口地址为 0x20. */
    	out_byte(INT_M_CTLMASK,	INT_VECTOR_IRQ0);
    	/* Slave  8259, ICW2. 设置 '从8259' 的中断入口地址为 0x28 */
    	out_byte(INT_S_CTLMASK,	INT_VECTOR_IRQ8);
    	/* Master 8259, ICW3. IR2 对应 '从8259'. */
    	out_byte(INT_M_CTLMASK,	0x4);
    	/* Slave  8259, ICW3. 对应 '主8259' 的 IR2. */
    	out_byte(INT_S_CTLMASK,	0x2);
    	/* Master 8259, ICW4. */
    	out_byte(INT_M_CTLMASK,	0x1);
    	/* Slave  8259, ICW4. */
    	out_byte(INT_S_CTLMASK,	0x1);
    	/* Master 8259, OCW1.  */
    	out_byte(INT_M_CTLMASK,	0xFF);
    	/* Slave  8259, OCW1.  */
    	out_byte(INT_S_CTLMASK,	0xFF);
    }
    

    out_byte的函数体位于kliba.asm中

    global	out_byte
    global	in_byte
    
    ; ========================================================================
    ;                  void out_byte(u16 port, u8 value);
    ; ========================================================================
    out_byte:
    	mov	edx, [esp + 4]		; port
    	mov	al, [esp + 4 + 4]	; value
    	out	dx, al
    	nop	; 一点延迟
    	nop
    	ret
    
    ; ========================================================================
    ;                  u8 in_byte(u16 port);
    ; ========================================================================
    in_byte:
    	mov	edx, [esp + 4]		; port
    	xor	eax, eax
    	in	al, dx
    	nop	; 一点延迟
    	nop
    	ret
    

    现在,该是把这些中断和异常的处理程序统统添加上的时候了。

    global	divide_error
    global	single_step_exception
    global	nmi
    global	breakpoint_exception
    global	overflow
    global	bounds_check
    ...
    
    	lidt	[idt_ptr]
    
    ...
    ; 中断和异常 -- 异常
    divide_error:
    	push	0xFFFFFFFF	; no err code
    	push	0		; vector_no	= 0
    	jmp	exception
    single_step_exception:
    	push	0xFFFFFFFF	; no err code
    	push	1		; vector_no	= 1
    	jmp	exception
    nmi:
    	push	0xFFFFFFFF	; no err code
    	push	2		; vector_no	= 2
    	jmp	exception
    breakpoint_exception:
    	push	0xFFFFFFFF	; no err code
    	push	3		; vector_no	= 3
    	jmp	exception
    overflow:
    	push	0xFFFFFFFF	; no err code
    	push	4		; vector_no	= 4
    	jmp	exception
    ...
    exception:
    	call	exception_handler
    	add	esp, 4*2	; 让栈顶指向 EIP,堆栈中从顶向下依次是:EIP、CS、EFLAGS
    	hlt
    

    异常发生时堆栈的变化情况是,中断或异常发生时eflags、cs、eip已经被压栈,如果有错误码的话,错误码也已经被压栈。

    所以我们对异常处理的总体思想是,如果有错误码,则直接把向量号压栈,然后执行一个函数exception_handler;如果没有错误码,则先在栈中压入一个0xFFFFFFFF,再把向量号压栈并随后执行exception_handler。

    函数exception_hanlder()的原型是这样的:

    void exception_handler(int vec_no,int err_code,int eip,int cs,int eflags);
    

    由于C调用约定是调用者恢复堆栈,所以不用担心exception_handler会破坏堆栈中的eip、cs以及eflags。

    现在我们已经有了异常处理函数,该是设置IDT的时候了。设置IDT的代码放进函数init_prot()中,它也位于protect.c中。protect.c通篇几乎只调用一个函数,就是init_idt_desc(),它用来初始化一个门描述符。其中用到的函数指针类型是这样定义的:

    typedef	void	(*int_handler)	();
    

    在init_prot()中,所有描述符都被初始化成中断门。DA_386IGate表示中断门。

    Intel为我们准备了一个指令叫做ud2,能够产生一个#UD异常。

    编译:

    make image

    运行结果如下:

    一个码农的日常 

    源码

  • 相关阅读:
    选择器
    【兼容】text
    SQL2008的数据更新跟踪测试
    拖拽文件到RichEdit的后期处理
    Web Browser 的扩展
    URL Protocol Handler
    asterisk1.6异步脚本
    CSDNER如何才能做到"最不缺的是技术"
    关于SQL语句Count的一点细节
    TG787 脚本
  • 原文地址:https://www.cnblogs.com/joey-hua/p/5418380.html
Copyright © 2011-2022 走看看