中断处理程序

前言

博客记录《操作系统真象还原》第七章实验的操作~

实验环境:ubuntu18.04+VMware , Bochs下载安装

实验内容

  1. 编写中断处理程序( 操作 8259A 打开中断)。
  2. 改进中断处理程序(在汇编版本的 intrXXentry 中调用 C 语言版本的中断处理函数)。
  3. 用计数器/定时器 8253 来设置时钟中断发生的频率。(给中断信号提速)。

前置知识

中断概念

中断处理(中断)的概念:由于 CPU 获知了计算机中发生的某些事,CPU 暂停正在执行的程序,转而去执行处理该事件的程序, 当这段程序执行完毕后,CPU 继续执行刚才的程序。整个过程称为中断处理,也称为中断。

OS 操作系统是中断驱动的。没有中断,操作系统几乎什么都做不了!

中断分类

中断可以分为以下2种类型。

  1. 外中断:来自 CPU 外部的中断就称为外部中断。
  2. 内中断:来自 CPU 内部的中断称为内部中断。

中断机制的本质:中断机制的本质是传入一个中断信号后,调用相应的中断处理程序。所以,CPU 将来自外部设备、内部指令的各种中断类型都归结为一种管理方式,即为每个中断信号分配一个整数,用此整数作为中断的 ID(整数即中断向量),然后用此 ID 作为中断描述符表中的索引,从而找到对应的表项,进而从中找到对应的中断处理程序。

外中断

外部中断是指来自 CPU 外部的中断,而这个外部的中断源一定是某个硬件。因此,外部中断又称为硬件中断。

外部硬件的中断是通过两根信号线通知 CPU 的,分别是INTR(INTeRrupt)和 NMI(Non Maskable Interrupt)。如下图所示

在这里插入图片描述

因为任务有轻重缓急,因此可以再细分位可屏蔽中断(maskable interrupt)和不可屏蔽中断(non-maskable interrupt,NMI)。

  • 可屏蔽中断
    概念:此外部设备发出的中断,CPU 可以不理会,因为它不会让系统宕机。
    【注】CPU也可以理会,把中断分为上半部和下半部分开处理。

  • 不可屏蔽中断
    概念:紧急的中断,严重可让让 CPU 宕机。
    中断处理流程:CPU 收到中断后,需要得知道发生了什么事情才能执行相应的处理办法。这是通过中断向量号进而查中断向量表或中断描述符表(中断向量表是实模式下的中断处理程序数组,在保护模式下已经被中断描述符表代替)来实现的。

内中断

内部中断可分为软中断和异常。

  • 软中断:软中断是指由软件主动发起的中断。(ex. “int 8 位立即数”)
  • 异常:异常是指令执行期间 CPU 内部产生的错误引起的。(ex. “int3”)

实验操作

实验一

开启中断流程

  1. init_all 函数用来初始化所有的设备及数据结构。
  2. init_all 调用 idt_init 初始化中断相关的内容。
  3. 初始化分为2个部分完成,分别是 pic_init 和 idt_desc_init。其中,pic_init (Programmable Interrupt Controller )用来初始化可编程中断控制器 8259A ,ide_desc_init 用来初始化中断描述符表 IDT。
  4. idt_init 完成,便加载 IDT 啦

在这里插入图片描述
实验1用汇编语言实现中断处理程序,该程序保存在 kernel.S 文件中。

(base) user@ubuntu:/home/cooiboi/bochs/kernel$ sudo vim kernel.S

完成了中断处理程序的编写,咱现在要把它们安装到中断描述符表中。即,创建中断描述符表 IDT,安装中断处理程序。该程序保存在 interrupt.c 文件中(文件 interrupt.c 包含目前有关中断的内容)。

(base) user@ubuntu:/home/cooiboi/bochs/kernel$ sudo vim interrupt.c

完成中断相关的数据准备,接下来需要设置中断代理 8259A ,这里用到内联汇编实现端口 I/O 函数。io.h 声明有关端口操作的函数定义。

(base) user@ubuntu:/home/cooiboi/bochs/lib/kernel$ sudo vim io.h

接着,需要定义初始化相关的工作的文件,即init_all。

(base) user@ubuntu:/home/cooiboi/bochs/kernel$ sudo vim init.c

init_all 是由 main.c 中的主函数 main 调用的,更新 main 函数。

(base) user@ubuntu:/home/cooiboi/bochs/kernel$ sudo vim  main.c

完成剩下文件的创建

(base) user@ubuntu:/home/cooiboi/bochs/kernel$ sudo vim global.h
(base) user@ubuntu:/home/cooiboi/bochs/kernel$ sudo vim init.h
(base) user@ubuntu:/home/cooiboi/bochs/kernel$ sudo vim interrupt.h

查看kernel下文件

(base) user@ubuntu:/home/cooiboi/bochs/kernel$ ls
global.h  init.h       interrupt.h  kernel.S
init.c    interrupt.c  kernel.bin   main.c

新建 build 文件夹存放输出文件(将所有目标文件和编译后的内核文件)

(base) user@ubuntu:/home/cooiboi/bochs$ sudo mkdir build

完成基本文件创建工作后,现在进入到编译、链接、写入磁盘的步骤。

编译工作

sudo nasm -f elf -o build/print.o lib/kernel/print.S
sudo nasm -f elf -o build/kernel.o kernel/kernel.S
(base) user@ubuntu:/home/cooiboi/bochs$ sudo nasm -f elf -o build/print.o lib/kernel/print.S
(base) user@ubuntu:/home/cooiboi/bochs$ sudo nasm -f elf -o build/kernel.o kernel/kernel.S
sudo gcc -m32 -I lib/kernel/ -m32 -I lib/ -m32 -I kernel/ -c -fno-builtin -o build/main.o kernel/main.c
sudo gcc -m32 -I lib/kernel/ -m32 -I lib/ -m32 -I kernel/ -c -fno-builtin -o build/interrupt.o kernel/interrupt.c
sudo gcc -m32 -I lib/kernel/ -m32 -I lib/ -m32 -I kernel/ -c -fno-builtin -o build/init.o kernel/init.c
(base) user@ubuntu:/home/cooiboi/bochs$ sudo gcc -m32 -I lib/kernel/ -m32 -I lib/ -m32 -I kernel/ -c -fno-builtin -o build/main.o kernel/main.c
(base) user@ubuntu:/home/cooiboi/bochs$ sudo gcc -m32 -I lib/kernel/ -m32 -I lib/ -m32 -I kernel/ -c -fno-builtin -o build/interrupt.o kernel/interrupt.c
(base) user@ubuntu:/home/cooiboi/bochs$ sudo gcc -m32 -I lib/kernel/ -m32 -I lib/ -m32 -I kernel/ -c -fno-builtin -o build/init.o kernel/init.c

链接工作

这里注意新 kernel.bin 的目录,是在 build 目录下

sudo ld -m elf_i386 -Ttext 0xc0001500 -e main -o build/kernel.bin build/main.o build/init.o build/interrupt.o build/print.o build/kernel.o
(base) user@ubuntu:/home/cooiboi/bochs$ sudo ld -m elf_i386 -Ttext 0xc0001500 -e main -o build/kernel.bin build/main.o build/init.o build/interrupt.o build/print.o build/kernel.o

写入磁盘

sudo dd if=/home/cooiboi/bochs/build/kernel.bin of=/home/cooiboi/bochs/boot/hd60M.img bs=512 count=200 seek=9 conv=notrunc
(base) user@ubuntu:/home/cooiboi/bochs$ sudo dd if=/home/cooiboi/bochs/build/kernel.bin of=/home/cooiboi/bochs/boot/hd60M.img bs=512 count=200 seek=9 conv=notrunc
14+1 records in
14+1 records out
7236 bytes (7.2 kB, 7.1 KiB) copied, 0.00014881 s, 48.6 MB/s

如果硬盘内容存在问题可以删掉重新写入,下面是之前写入的内容(贴出来方便实现)

sudo dd if=/home/cooiboi/bochs/boot/mbr.bin of=/home/cooiboi/bochs/boot/hd60M.img bs=512 count=1 conv=notrunc
sudo dd if=/home/cooiboi/bochs/boot/loader.bin of=/home/cooiboi/bochs/boot/hd60M.img bs=512 count=3 seek=2 conv=notrunc

启动Bochs

sudo bin/bochs -f boot/bochsrc.disk
(base) user@ubuntu:/home/cooiboi/bochs$ sudo bin/bochs -f boot/bochsrc.disk

在这里插入图片描述

让我们看看安装后的中断描述符的样子八!命令:info idt

在这里插入图片描述

  • 第一列是中断门描述符的序号,共 0x20 个。
  • Interrupt Gate target 是门描述符中所指向的中断处理程序地址,用选择子:偏移量的形式表示。

假设查看某个中断门描述符,可以使用info idt 序号命令。【下图所示中断向量号 0x20 对应的中断门描述符】

在这里插入图片描述

实验二

改进中断处理程序,在汇编版本的 intrXXentry 中调用 C 语言版本的中断处理函数。

修改 interrupt.c 和 kernel.S 文件

(base) user@ubuntu:/home/cooiboi/bochs/kernel$ sudo vim interrupt.c
(base) user@ubuntu:/home/cooiboi/bochs/kernel$ sudo vim  kernel.S 

之后的步骤和实验一的一模一样~

运行图中不断打印“int vector: 0x20”(只打开了时钟中断,并且时钟的中断向量号是 0x20)

在这里插入图片描述

实验三

新建device 目录下,存放设备代码。

(base) user@ubuntu:/home/cooiboi/bochs$ sudo mkdir  device

创建 timer.c 和 timer.h。

(base) user@ubuntu:/home/cooiboi/bochs/device$ sudo vim timer.c
(base) user@ubuntu:/home/cooiboi/bochs/device$ sudo vim timer.h

接下来,把 timer_init 函数加在文件 init.c 中

(base) user@ubuntu:/home/cooiboi/bochs/kernel$ sudo vim  init.c

完成基本文件创建工作后,现在进入到编译、链接、写入磁盘的步骤。

下面命令均在/home/cooiboi/bochs$下执行。

编译

sudo nasm -f elf -o build/print.o lib/kernel/print.S
sudo nasm -f elf -o build/kernel.o kernel/kernel.S
sudo gcc -m32 -I lib/kernel -c -o build/timer.o device/timer.c
sudo gcc -m32 -I lib/kernel/ -m32 -I lib/ -m32 -I kernel/ -c -fno-builtin -o build/main.o kernel/main.c
sudo gcc -m32 -I lib/kernel/ -m32 -I lib/ -m32 -I kernel/ -c -fno-builtin -o build/interrupt.o kernel/interrupt.c
sudo gcc -m32 -I lib/kernel/ -m32 -I lib/ -m32 -I kernel/ -c -fno-builtin -o build/init.o kernel/init.c

链接工作

sudo ld -m elf_i386 -Ttext 0xc0001500 -e main -o build/kernel.bin build/main.o build/init.o build/interrupt.o build/print.o build/kernel.o build/timer.o

写入磁盘

sudo dd if=/home/cooiboi/bochs/build/kernel.bin of=/home/cooiboi/bochs/boot/hd60M.img bs=512 count=200 seek=9 conv=notrunc

启动Bochs

sudo bin/bochs -f boot/bochsrc.disk

在这里插入图片描述
参考资料

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在 XDMA 设备中,中断处理程序是用来处理 DMA 完成和错误事件的。中断处理程序的实现通常会包括以下步骤: 1. 确定中断源:读取 XDMA 的中断状态寄存器,以确定触发中断的源头。 2. 处理中断:根据中断源,执行相应的处理操作。例如,在 DMA 完成中断时,可以读取 DMA 完成寄存器来确定已经完成的传输数量。 3. 清除中断:在处理中断后,需要清除中断状态寄存器。这可以通过写入相应的位来完成。 以下是一个示例 XDMA 中断处理程序的代码: ```c void xdma_irq_handler(void *data) { struct xdma_dev *dev = data; u32 irq_status; irq_status = xdma_read(dev, XDMA_IRQ_STATUS_OFFSET); if (irq_status & XDMA_IRQ_COMPLETE_MASK) { u32 complete = xdma_read(dev, XDMA_DMA_COMPLETE_OFFSET); /* process DMA completion */ xdma_write(dev, XDMA_IRQ_STATUS_OFFSET, XDMA_IRQ_COMPLETE_MASK); } if (irq_status & XDMA_IRQ_ERROR_MASK) { u32 error = xdma_read(dev, XDMA_DMA_ERROR_OFFSET); /* process DMA error */ xdma_write(dev, XDMA_IRQ_STATUS_OFFSET, XDMA_IRQ_ERROR_MASK); } } ``` 在这个例子中,中断处理程序首先读取中断状态寄存器,然后根据中断源执行相应的操作。最后,它会清除中断状态寄存器,以便下一次中断可以正常触发。请注意,中断处理程序应该是尽可能快的,以免影响系统的性能。 ### 回答2: xdma的中断处理程序主要用于处理xdma设备发生中断时的相关操作。以下是xdma的中断处理程序的基本步骤: 1. 中断服务程序的入口:当xdma设备触发中断时,中央处理器(CPU)会跳转到中断服务程序的入口,即中断处理程序的起始地址。 2. 寄存器保存和恢复:在处理中断之前,中断处理程序需要保存当前的寄存器状态,以免发生寄存器数据丢失。保存的寄存器包括CPU寄存器、状态寄存器、栈指针等。当中断处理完成后,需要将之前保存的寄存器状态恢复。 3. 中断处理逻辑:中断处理程序根据中断类型和中断原因来执行相应的处理逻辑。对于xdma设备中断,可能的处理逻辑包括: - 确定中断源:中断处理程序需要确定是哪个xdma设备发生了中断,以便进行后续的处理。 - 中断状态处理:读取中断状态寄存器,判断中断原因,例如传输完成、传输错误等。 - 数据处理:根据中断原因,处理中断所涉及的数据。比如,如果是传输完成,可以触发相应的后续操作,如通知应用程序传输完成。 - 清除中断标志:处理中断后,需要清除相应的中断标志或寄存器状态,以准备下一次中断。 4. 中断结束:中断处理程序执行完成后,会跳转回中断服务程序的返回地址,中央处理器继续执行原有的程序流程。 总之,xdma的中断处理程序是用来处理xdma设备中断的,其主要包括保存和恢复寄存器状态、中断处理逻辑和中断结束等步骤。在中断处理过程中,通过读取中断状态寄存器来确定中断原因,并根据不同的中断原因执行相应的处理逻辑。处理完成后,需要清除中断标志以便下一次中断的准备。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值