---
# System prepended metadata

title: 'CH 07 : 中斷'

---

Chapter 7：中斷
===
:::info
這是讀書筆記

![679544](https://hackmd.io/_uploads/HJB-ZFKV-l.jpg =30%x)

作者：鄭鋼
出版社：佳魁資訊股份有限公司
出版日期：2017/05/31
:::
>[time=2025-07-02]
---

# 中斷簡介

## 硬體中斷

* maskable interrupt: 可通過在中斷封鎖暫存器(eflags:IF)中設定位遮罩來關閉。
* non-maskable interrupt (NMI): 無法通過在中斷封鎖暫存器中設定位遮罩來關閉。

參考網址：https://zh.wikipedia.org/zh-tw/%E4%B8%AD%E6%96%B7

## 軟體中斷

* Normal interrupt: 
    * system call.
* Exception interrupt:
    * Fault: 這種例外通常是可以更正的，而且在更正後，程式應該可以繼續無誤地進行。在例外處理完，返回原程式時，會重新執行原來造成 fault 的指令。 e.g. page fault.
    * Trap: 它的返回位址是被 trap 的指令的下一個指令。在例外返回後，程式可以正確無誤地斷續執行。e.g. 偵錯中斷點.
    * Abort: 這種例外是非常嚴重的錯誤，沒有辦法返回原程式繼續執行。

# Interrupt Descriptor Table(IDT)

## Real Mode: Interrupt Vector Table(IVT)
https://zh.wikipedia.org/zh-tw/BIOS%E4%B8%AD%E6%96%B7%E5%91%BC%E5%8F%AB

0x0 - 0x3FF: 1KB
每個 item 4B，共可以擁有 256 個中斷向量。

## Protected Mode: Interrupt Descriptor Table(IDT)
![3](https://hackmd.io/_uploads/Hk9r9YlSle.png)

![gwZRF](https://hackmd.io/_uploads/HJ996FgHex.png)

IDT limit 16 bits=64KB
每個 item 8B，共可以擁有 8192 個中斷描述符表。

## 中斷處理過程
![fig9-4](https://hackmd.io/_uploads/rkMVKYgrxl.gif)

# 中斷控制器 8259A

![ganCauJ](https://hackmd.io/_uploads/rJNV9Rkree.png)


![8259a-1](https://hackmd.io/_uploads/ByqCFAJrlx.jpg)


![截圖 2025-06-30 下午5.31.03](https://hackmd.io/_uploads/SJEotCkBgg.png)

## Source code

```c=
/* 初始化可编程中断控制器8259A */
static void pic_init(void) {

    /* 初始化主片 */
    outb (PIC_M_CTRL, 0x11);   // ICW1: 边沿触发,级联8259, 需要ICW4.
    outb (PIC_M_DATA, 0x20);   // ICW2: 起始中断向量号为0x20,也就是IR[0-7] 为 0x20 ~ 0x27.
    outb (PIC_M_DATA, 0x04);   // ICW3: IR2接从片. 
    outb (PIC_M_DATA, 0x01);   // ICW4: 8086模式, 正常EOI

    /* 初始化从片 */
    outb (PIC_S_CTRL, 0x11);	// ICW1: 边沿触发,级联8259, 需要ICW4.
    outb (PIC_S_DATA, 0x28);	// ICW2: 起始中断向量号为0x28,也就是IR[8-15] 为 0x28 ~ 0x2F.
    outb (PIC_S_DATA, 0x02);	// ICW3: 设置从片连接到主片的IR2引脚
    outb (PIC_S_DATA, 0x01);	// ICW4: 8086模式, 正常EOI

    /* 打开主片上IR0,也就是目前只接受时钟产生的中断 */
    outb (PIC_M_DATA, 0xfe);
    outb (PIC_S_DATA, 0xff);

    put_str("   pic_init done\n");
}
```

# 中斷處理常式

## Interrupt Service Routine by using assembly

```asm=
[bits 32]
%define ERROR_CODE nop		 ; 若在相关的异常中cpu已经自动压入了错误码,为保持栈中格式统一,这里不做操作.
%define ZERO push 0		 ; 若在相关的异常中cpu没有压入错误码,为了统一栈中格式,就手工压入一个0

extern put_str			 ;声明外部函数

section .data
intr_str db "interrupt occur!", 0xa, 0
global intr_entry_table
intr_entry_table:

%macro VECTOR 2
section .text
intr%1entry:		 ; 每个中断处理程序都要压入中断向量号,所以一个中断类型一个中断处理程序，自己知道自己的中断向量号是多少
   %2
   push intr_str
   call put_str
   add esp,4			 ; 跳过参数

   ; 如果是从片上进入的中断,除了往从片上发送EOI外,还要往主片上发送EOI 
   mov al,0x20                   ; 中断结束命令EOI
   out 0xa0,al                   ; 向从片发送
   out 0x20,al                   ; 向主片发送

   add esp,4			 ; 跨过error_code
   iret				 ; 从中断返回,32位下等同指令iretd

section .data
   dd    intr%1entry	 ; 存储各个中断入口程序的地址，形成intr_entry_table数组
%endmacro

VECTOR 0x00,ZERO
VECTOR 0x01,ZERO
VECTOR 0x02,ZERO
VECTOR 0x03,ZERO 
VECTOR 0x04,ZERO
VECTOR 0x05,ZERO
VECTOR 0x06,ZERO
VECTOR 0x07,ZERO 
VECTOR 0x08,ERROR_CODE
VECTOR 0x09,ZERO
VECTOR 0x0a,ERROR_CODE
VECTOR 0x0b,ERROR_CODE 
VECTOR 0x0c,ZERO
VECTOR 0x0d,ERROR_CODE
VECTOR 0x0e,ERROR_CODE
VECTOR 0x0f,ZERO 
VECTOR 0x10,ZERO
VECTOR 0x11,ERROR_CODE
VECTOR 0x12,ZERO
VECTOR 0x13,ZERO 
VECTOR 0x14,ZERO
VECTOR 0x15,ZERO
VECTOR 0x16,ZERO
VECTOR 0x17,ZERO 
VECTOR 0x18,ERROR_CODE
VECTOR 0x19,ZERO
VECTOR 0x1a,ERROR_CODE
VECTOR 0x1b,ERROR_CODE 
VECTOR 0x1c,ZERO
VECTOR 0x1d,ERROR_CODE
VECTOR 0x1e,ERROR_CODE
VECTOR 0x1f,ZERO 
VECTOR 0x20,ZERO
```

## 建立 IDT

```c=
#include "interrupt.h"
#include "stdint.h"
#include "global.h"
...

#define IDT_DESC_CNT 0x21	 // 目前总共支持的中断数

/*中断门描述符结构体*/
struct gate_desc {
    uint16_t    func_offset_low_word;
    uint16_t    selector;
    uint8_t     dcount;   //此项为双字计数字段，是门描述符中的第4字节。此项固定值，不用考虑
    uint8_t     attribute;
    uint16_t    func_offset_high_word;
};

// 静态函数声明,非必须
static void make_idt_desc(struct gate_desc* p_gdesc, uint8_t attr, intr_handler function);
static struct gate_desc idt[IDT_DESC_CNT];   // idt是中断描述符表,本质上就是个中断门描述符数组

extern intr_handler intr_entry_table[IDT_DESC_CNT];	    // 声明引用定义在kernel.S中的中断处理函数入口数组

...

/* 创建中断门描述符 */
static void make_idt_desc(struct gate_desc* p_gdesc, uint8_t attr, intr_handler function) { 
    p_gdesc->func_offset_low_word = (uint32_t)function & 0x0000FFFF;
    p_gdesc->selector = SELECTOR_K_CODE;
    p_gdesc->dcount = 0;
    p_gdesc->attribute = attr;
    p_gdesc->func_offset_high_word = ((uint32_t)function & 0xFFFF0000) >> 16;
}

/*初始化中断描述符表*/
static void idt_desc_init(void) {
    int i;
    for (i = 0; i < IDT_DESC_CNT; i++) {
        make_idt_desc(&idt[i], IDT_DESC_ATTR_DPL0, intr_entry_table[i]); 
    }
    put_str("   idt_desc_init done\n");
}

/*完成有关中断的所有初始化工作*/
void idt_init() {
    put_str("idt_init start\n");
    idt_desc_init();	   // 初始化中断描述符表
    pic_init();		   // 初始化8259A

    ...
}

```

## I/O port function by inline assembly

insw: https://c9x.me/x86/html/file_module_x86_id_141.html
outsw: https://dinlon5566.com/IA32/instruct32_hh/vc220.html

## Setting 8259A

```c=
/* 初始化可编程中断控制器8259A */
static void pic_init(void) {

    /* 初始化主片 */
    outb (PIC_M_CTRL, 0x11);   // ICW1: 边沿触发,级联8259, 需要ICW4.
    outb (PIC_M_DATA, 0x20);   // ICW2: 起始中断向量号为0x20,也就是IR[0-7] 为 0x20 ~ 0x27.
    outb (PIC_M_DATA, 0x04);   // ICW3: IR2接从片. 
    outb (PIC_M_DATA, 0x01);   // ICW4: 8086模式, 正常EOI

    /* 初始化从片 */
    outb (PIC_S_CTRL, 0x11);	// ICW1: 边沿触发,级联8259, 需要ICW4.
    outb (PIC_S_DATA, 0x28);	// ICW2: 起始中断向量号为0x28,也就是IR[8-15] 为 0x28 ~ 0x2F.
    outb (PIC_S_DATA, 0x02);	// ICW3: 设置从片连接到主片的IR2引脚
    outb (PIC_S_DATA, 0x01);	// ICW4: 8086模式, 正常EOI

    /* 打开主片上IR0,也就是目前只接受时钟产生的中断 */
    outb (PIC_M_DATA, 0xfe);
    outb (PIC_S_DATA, 0xff);

    put_str("   pic_init done\n");
}
```

## 載入 IDT 開啟中斷

```c=
/* 加载idt */
    uint64_t idt_operand = ((sizeof(idt) - 1) | 
                            ((uint64_t)(uint32_t)idt << 16));
    asm volatile("lidt %0" : : "m" (idt_operand));
    put_str("idt_init done\n");
```

## Source Code

https://github.com/yifengyou/os-elephant/tree/master/code/c07/a

## Compile

```sh=
# build boot
nasm boot/mbr.S \
        -o out/mbr.bin \
        -I boot/inc/

nasm boot/loader.S \
        -o out/loader.bin \
        -I boot/inc/


# build library
nasm lib/kernel/print.S \
        -o out/print.o \
        -f elf


# build kernel
nasm kernel/kernel.S \
        -o out/kernel.o \
        -f elf

x86_64-linux-gnu-gcc kernel/interrupt.c \
        -o out/interrupt.o \
        -c -m32 \
        -fno-stack-protector \
        -I lib/inc \
        -I lib/kernel/inc/

x86_64-linux-gnu-gcc kernel/init.c \
        -o out/init.o \
        -c -m32 \
        -fno-stack-protector \
        -I lib/inc \
        -I lib/kernel/inc/

x86_64-linux-gnu-gcc kernel/main.c \
        -o out/main.o \
        -c -m32 \
        -fno-stack-protector \
        -I lib/inc \
        -I lib/kernel/inc/

x86_64-linux-gnu-ld out/main.o out/print.o out/init.o out/interrupt.o out/kernel.o\
        -o out/kernel.bin \
        -Ttext 0xc0001500 \
        -e main \
        -m elf_i386 \
        -z noexecstack

```

>[!Warning]hidden symbol `__stack_chk_fail_local' isn't defined
>https://blog.csdn.net/weixin_51259834/article/details/128335475
>-fno-stack-protector

## Result

![截圖 2025-07-01 中午12.45.55](https://hackmd.io/_uploads/ryl4_J-Blx.png)

# 中斷處理常式 加強版

## Source Code

https://github.com/yifengyou/os-elephant/tree/master/code/c07/a

## Result

![截圖 2025-07-02 上午9.36.44](https://hackmd.io/_uploads/SyxLpZfBgg.png)

# 偵錯實例：中斷發生前後，Stack的完整過程

文字敘述有看完理解了，有空再練習吧！

# 計時器 8253

## 簡介

* Programmable Interval Timer(PIT)
    https://wiki.osdev.org/Programmable_Interval_Timer

![截圖 2025-07-02 上午11.54.15](https://hackmd.io/_uploads/H1NF67MSxe.png)

For 0x43 Mode register:
![截圖 2025-07-02 上午11.54.43](https://hackmd.io/_uploads/H1lspmGSxx.png)

![截圖 2025-07-02 下午12.13.17](https://hackmd.io/_uploads/HJBWGNzSex.png)

![截圖 2025-07-02 下午12.15.52](https://hackmd.io/_uploads/rJsiMEGHxl.png)

![截圖 2025-07-02 下午12.16.40](https://hackmd.io/_uploads/Sk5aG4zSeg.png)

![截圖 2025-07-02 下午12.17.17](https://hackmd.io/_uploads/S1WlQ4fBlx.png)

![截圖 2025-07-02 下午12.17.55](https://hackmd.io/_uploads/SylG7EfHlg.png)

![截圖 2025-07-02 下午12.18.24](https://hackmd.io/_uploads/rygNXVfSex.png)

## Source Code

https://github.com/yifengyou/os-elephant/tree/master/code/c07/c

## Result

![截圖 2025-07-02 下午1.56.19](https://hackmd.io/_uploads/r1DXcSMBgl.png)



