🐳
uCore OS(on RISC-V64)实验指导书
  • Introduction
  • LAB0:ready~go!
    • 实验目的
    • 实验内容
    • 前导知识
      • 了解uCore
      • 了解RISC-V
      • 了解OS实验
      • 了解实验环境
      • 了解开发调试基本工具
      • 了解硬件模拟器
    • 配置环境
      • 安装虚拟环境
      • 安装开发工具
      • 安装硬件模拟器
      • 安装调试工具
  • LAB0.5:最小可执行内核
    • 实验目的
    • 实验内容
    • 练习
    • 内存布局
    • 链接脚本
    • 真正的入口点
    • 从SBI到stdio
    • 编译运行
    • 项目组成与执行流
  • LAB1:中断机制
    • 实验目的
    • 实验内容
    • 练习
    • RISC-V中断相关
    • 上下文处理
    • 中断处理程序
    • 时钟中断
    • 项目组成与执行流
  • LAB2:物理内存管理
    • 实验目的
    • 实验内容
    • 练习
    • 地址与页表
    • 物理内存探测
    • 以页为单位管理物理内存
    • 页面分配算法
    • 项目组成与执行流
  • LAB3:虚拟内存管理
    • 实验目的
    • 实验内容
    • 练习
    • 页面置换
    • PageFault
    • 使用多级页表
    • 页面置换机制
    • FIFO置换算法
    • 项目组成与执行流
  • LAB4:进程管理
    • 实验目的
    • 实验内容
    • 练习
    • 进程与线程
    • 相关结构体
    • 进程模块初始化
    • 进程切换
    • 项目组成与执行流
  • LAB5:用户程序
    • 实验目的
    • 实验内容
    • 练习
    • 用户进程
    • 用户程序
    • 创建并执行用户进程
    • 系统调用
    • 用户进程的退出和等待
    • 项目组成与执行流
  • LAB6:进程调度
    • 实验目的
    • 实验内容
    • 练习
    • 进程状态
    • 再次认识进程切换
    • 调度算法框架
    • 项目组成与执行流
  • LAB7:同步互斥
    • 实验目的
    • 实验内容
    • 练习
    • 同步互斥的基本概念
    • 信号量
    • 条件变量与管程
    • 项目组成与执行流
  • LAB8:文件系统
    • 实验目的
    • 实验内容
    • 练习
    • 文件系统介绍
    • 文件系统抽象层VFS
    • 硬盘文件系统SFS
    • 设备即文件
    • 从中断到终端
    • 项目组成与执行流
由 GitBook 提供支持
在本页
  • scause
  • 初始化
  • 处理

这有帮助吗?

  1. LAB1:中断机制

中断处理程序

scause

当处理自陷时, cause CSR中被写入一个指示导致 自陷的事件的代码。如果自陷由中断引起,则置上中断位。“异常代码”字段包含指示最后一个异常的代 码。具体的中断/异常映射关系,见中文手册100页。

初始化

中断处理需要初始化,所以我们在init.c里调用一些初始化的函数

// kern/init/init.c
#include <trap.h>
int kern_init(void) {
    extern char edata[], end[];
    memset(edata, 0, end - edata);

    cons_init();  // init the console

    const char *message = "(THU.CST) os is loading ...\n";
    cprintf("%s\n\n", message);

    print_kerninfo();

    // grade_backtrace();

    //trap.h的函数,初始化中断
    idt_init();  // init interrupt descriptor table

    //clock.h的函数,初始化时钟中断
    clock_init();  
    //intr.h的函数,使能中断
    intr_enable();  

    // LAB1: CAHLLENGE 1 If you try to do it, uncomment lab1_switch_test()
    // user/kernel mode switch test
    // lab1_switch_test();
    /* do nothing */
    while (1)
        ;
}
// kern/trap/trap.c
void idt_init(void) {
    extern void __alltraps(void);
    //约定:若中断前处于S态,sscratch为0
    //若中断前处于U态,sscratch存储内核栈地址
    //那么之后就可以通过sscratch的数值判断是内核态产生的中断还是用户态产生的中断
    //我们现在是内核态所以给sscratch置零
    write_csr(sscratch, 0);
    //我们保证__alltraps的地址是四字节对齐的,将__alltraps这个符号的地址直接写到stvec寄存器
    write_csr(stvec, &__alltraps);
}
//kern/driver/intr.c
#include <intr.h>
#include <riscv.h>
/* intr_enable - enable irq interrupt, 设置sstatus的Supervisor中断使能位 */
void intr_enable(void) { set_csr(sstatus, SSTATUS_SIE); }
/* intr_disable - disable irq interrupt */
void intr_disable(void) { clear_csr(sstatus, SSTATUS_SIE); }

处理

trap.c的中断处理函数trap, 实际上把中断处理,异常处理的工作分发给了interrupt_handler(),exception_handler(), 这些函数再根据中断或异常的不同类型来处理。

// kern/trap/trap.c
/* trap_dispatch - dispatch based on what type of trap occurred */
static inline void trap_dispatch(struct trapframe *tf) {
    //scause的最高位是1,说明trap是由中断引起的
    if ((intptr_t)tf->cause < 0) {
        // interrupts
        interrupt_handler(tf);
    } else {
        // exceptions
        exception_handler(tf);
    }
}

/* *
 * trap - handles or dispatches an exception/interrupt. if and when trap()
 * returns,
 * the code in kern/trap/trapentry.S restores the old CPU state saved in the
 * trapframe and then uses the iret instruction to return from the exception.
 * */
void trap(struct trapframe *tf) { trap_dispatch(tf); }

interrupt_handler()和exception_handler()的实现还比较简单,只是简单地根据scause的数值更仔细地分了下类,做了一些输出就直接返回了。switch里的各种case, 如IRQ_U_SOFT,CAUSE_USER_ECALL,是riscv ISA 标准里规定的。我们在riscv.h里定义了这些常量。我们接下来主要关注时钟中断的处理。

在这里我们对时钟中断进行了一个简单的处理,即每次触发时钟中断的时候,我们会给一个计数器加一,并且设定好下一次时钟中断。当计数器加到100的时候,我们会输出一个100ticks表示我们触发了100次时钟中断。通过在模拟器中观察输出我们即刻看到是否正确触发了时钟中断,从而验证我们实现的异常处理机制。

上一页上下文处理下一页时钟中断

最后更新于4年前

这有帮助吗?