进入内核-中断开启

目前为止的OS流程

Init1 - > 中断初始化

cpp 复制代码
/*完成有关中断的所有初始化工作*/
void idt_init() {
   put_str("idt_init start\n");
   idt_desc_init();       // 初始化中断描述符表
   exception_init();       // 异常名初始化并注册通常的中断处理函数
   pic_init();           // 初始化8259A

   /* 加载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");
}
中断描述符表

中断描述符表固定在内存中,通过IDTR寄存器指明内存地址。当CPU接受到中断时,会根据中断向量检索对应的描述符。执行对应的中断处理程序。

为了简化实现,中断描述符表中只有中断门描述符。由低位地址(16位)+选择子(8位)+dount+attribute(8位 10001110)+高位地址(16位)组成。

cpp 复制代码
/*中断门描述符结构体*/
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) { 
   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, lastindex = IDT_DESC_CNT - 1;
   for (i = 0; i < IDT_DESC_CNT; i++) {
      make_idt_desc(&idt[i], IDT_DESC_ATTR_DPL0, intr_entry_table[i]); 
   }
   
   make_idt_desc(&idt[lastindex],IDT_DESC_ATTR_DPL3,syscall_handler);
/* 单独处理系统调用,系统调用对应的中断门dpl为3,
 * 中断处理程序为单独的syscall_handler */
   put_str("   idt_desc_init done\n");
}

中断门描述符的函数是从129个函数指针数组中拿出的function,intr_entry_table每个指针指向了一个中断入口函数,执行流程是:

其中最后一个0x80中断特殊,是系统调用的通用入口,系统调用的attribute不同,DPL为3,是用户态可调用的。保证了用户程序可使用。

系统调用实现
bash 复制代码
//用户态使用
// 用户程序
int ret = syscall(SYS_WRITE, fd, buf, count);
//                     ↑      ↑   ↑    ↑
//                   eax    ebx ecx  edx

section .text   
global syscall_handler
syscall_handler:
    push 0
    
    push ds
    push es
    push fs
    push gs
    pushad
    
    push 0x80                 ; 不管是否需要参数,都一律压入中断向量号,调试时很方便
    push edx                 ;第三个参数
    push ecx                 ;第二个参数
    push ebx                 ;第一个参数
    
    call [syscall_table + eax*4]
    add  esp,12               ;清理 push edx/ecx/ebx
    
    mov [esp + 8*4],eax         ;返回值给放到eax中 ,修改栈中的eax值
    jmp intr_exit
中断处理函数

中断处理函数根据硬件和和向量号不同各不相同,所以提供两种注册方式。

exception_init为所有的中断向量提供兜底的基础中断处理函数。

register_handler为不同硬件初始化时自行注册各自的中断处理函数。

cpp 复制代码
/* 完成一般中断处理函数注册及异常名称注册 */
static void exception_init(void) {                // 完成一般中断处理函数注册及异常名称注册
   int i;
   for (i = 0; i < IDT_DESC_CNT; i++) {

/* idt_table数组中的函数是在进入中断后根据中断向量号调用的,
 * 见kernel/kernel.S的call [idt_table + %1*4] */
      idt_table[i] = general_intr_handler;            // 默认为general_intr_handler。
                                // 以后会由register_handler来注册具体处理函数。
      intr_name[i] = "unknown";                    // 先统一赋值为unknown 
   }
   intr_name[0] = "#DE Divide Error";
   intr_name[1] = "#DB Debug Exception";
   intr_name[2] = "NMI Interrupt";
   intr_name[3] = "#BP Breakpoint Exception";
   intr_name[4] = "#OF Overflow Exception";
   intr_name[5] = "#BR BOUND Range Exceeded Exception";
   intr_name[6] = "#UD Invalid Opcode Exception";
   intr_name[7] = "#NM Device Not Available Exception";
   intr_name[8] = "#DF Double Fault Exception";
   intr_name[9] = "Coprocessor Segment Overrun";
   intr_name[10] = "#TS Invalid TSS Exception";
   intr_name[11] = "#NP Segment Not Present";
   intr_name[12] = "#SS Stack Fault Exception";
   intr_name[13] = "#GP General Protection Exception";
   intr_name[14] = "#PF Page-Fault Exception";
   // intr_name[15] 第15项是intel保留项,未使用
   intr_name[16] = "#MF x87 FPU Floating-Point Error";
   intr_name[17] = "#AC Alignment Check Exception";
   intr_name[18] = "#MC Machine-Check Exception";
   intr_name[19] = "#XF SIMD Floating-Point Exception";
   //intr_name[80] = "SYSCALL INTR";
}

void register_handler(uint8_t vec_no,intr_handler function)
{
    //把相关向量号的注册函数指针放进去了
    idt_table[vec_no] = function;
}
PIC

初始化中断控制器8259A。

CPU只有两个中断源引脚,可屏蔽中断和不可屏蔽中断。但是实际PC有多个外设中断,所以需要8259A来管理中断。复用CPU的中断引脚。

提供了以下作用:

  • 中断复用
  • 优先级管理
  • 中断屏蔽
  • 中断向量生成
  • 级联控制
写入中断向量表地址
cpp 复制代码
    uint64_t idt_operand = ((sizeof(idt) - 1) | ((uint64_t)(uint32_t)idt << 16));
   asm volatile("lidt %0" : : "m" (idt_operand));
其余功能

开关中断+获得中断状态。

cpp 复制代码
enum intr_status intr_enable(void);
enum intr_status intr_disable(void);
enum intr_status intr_set_status(enum intr_status status);
enum intr_status intr_get_status(void);
相关推荐
小杍随笔1 小时前
【Rust桌面革命:Tauri×Dioxus——架构对决、实战拆解与2026选型杀招】
开发语言·架构·rust
计算机安禾1 小时前
【c++面向对象编程】第4篇:类与对象(三):拷贝构造函数与深浅拷贝问题
开发语言·c++·算法
j_xxx404_1 小时前
Linux共享内存原理与实战:从内核到C++实现|附源码
linux·运维·开发语言·c++·人工智能
步步为营DotNet1 小时前
.NET 11 中 C# 14 新特性在云原生微服务安全与性能优化的深度探索
云原生·c#·.net
金玉满堂@bj1 小时前
Python 后端开发 从零到就业完整教程(2026 企业级完整版)
开发语言·python
qq_269870431 小时前
claude code 终端安装
开发语言
XiYang-DING1 小时前
【Java】URL(Uniform Resource Locator)
java·开发语言
0xDevNull1 小时前
Java十道高频面试题
java·开发语言
Brilliantwxx1 小时前
【算法题】递归树+哈希表+分治异或+双指针
开发语言·c++·笔记·算法