中断编程实验

本文涉及的产品
应用型负载均衡 ALB,每月750个小时 15LCU
公网NAT网关,每月750个小时 15CU
网络型负载均衡 NLB,每月750个小时 15LCU
简介: 中断编程实验

引言

  • 本章节我们将会通过 2 个实验来深入理解保护模式下的中断

实验一

  • 实验目标:自定义一个保护模式下的软中断(int 0x80),使得在使用 int 0x80 中断后在屏幕上打印字符串 “int 0x80”
  • 实现思路

  • 直接开始吧,让我们先找个以前实现过的基础代码:loader.asm
  • 回顾代码的框架
boot.asm 跳转到 loader.asm,并打印 “Welcome to KOS.” 
      |
CODE16_START   ; 实模式,打印 “Loader...”
      |
CODE32_START   ; 进入保护模式,打印 “Enter protection”
  • 老规矩,先把最终实现代码给你们,再来一步一步讲解过程.最终代码:loader.asm
  • 先准备好 2 个中断服务程序,注意,中断服务程序必须用 iret 返回
  • DefaultHandler 函数用于默认中断处理函数,仅提供函数地址作用,可以不用实现函数内部功能;Int0x80Handler 函数就是我们本次实验的目标函数,内部实现打印 “int 0x80” 字符串
DefaultHandler:
    iret
DefaultHandler_Offset   equ     DefaultHandler-CODE32_START
Int0x80Handler:
    mov ebp, msg_int0x80_offset
    mov bl, 0x0F        ; 打印属性,黑底白字
    ; 坐标 (0, 3)
    mov dl, 0x00
    mov dh, 0x03
    call print_str_32
    iret
Int0x80Handler_Offset   equ     Int0x80Handler-CODE32_START
  • 接下来构建一个 IDT 中断描述符表,可以借用 【调用门】代码中门描述符的定义。共 256 个中断描述符
; 中断描述符表定义
;                选择子,          偏移地址,               参数个数,属性    
IDT_BASE : Gate  CODE32_SELECTOR,  DefaultHandler_Offset,  0,  DA_INTR_GATE
           Gate  CODE32_SELECTOR,  DefaultHandler_Offset,  0,  DA_INTR_GATE
           Gate  CODE32_SELECTOR,  DefaultHandler_Offset,  0,  DA_INTR_GATE
           ...(重复 256 次) 
IDT_LEN     equ     $ - IDT_BASE
  • 我们看到中断描述符表中有大量重复的代码,我们可以使用 rep 指令处理重复操作,rep 使用格式如下:
; 重复 n 次 xxx
%rep n
xxx
%endrep
  • 改动一下
IDT_BASE:
%rep 256
    Gate   CODE32_SELECTOR,  DefaultHandler_Offset,  0,       DA_INTR_GATE
    %endrep
IDT_LEN     equ     $ - IDT_BASE
  • int 0x80 中断在整个中断描述符表中排第 129 位,于是再次改动一下:
; IDT 中断描述符表定义
;          选择子,           偏移地址,             参数个数, 属性 
IDT_BASE: 
    %rep 128
    Gate   CODE32_SELECTOR,  DefaultHandler_Offset,  0,       DA_INTR_GATE
    %endrep
    Gate   CODE32_SELECTOR,  Int0x80Handler_Offset,  0,       DA_INTR_GATE
    %rep 127
    Gate   CODE32_SELECTOR,  DefaultHandler_Offset,  0,       DA_INTR_GATE
    %endrep
IDT_LEN     equ     $ - IDT_BASE
  • 中段描述符表 IDT 我们已经构建好了,参考全局描述符表加载(lgdt),我们需要告诉 CPU 哪片内存是 IDT,即 lidt 指令,其格式是 lidt [6 个字节的内存数据首地址]
  • 我们只需要先定义这 6 个字节的数据
IDT_PTR :
  dw   IDT_LEN - 1
  dd   IDT_BASE
  • 然后再使用 lidt 就可以了,注意:要放在实模式下调用

lidt [IDT_PTR]

  • 准备工作全部完成啦,下面就是在 32 位保护模式下调用 int 0x80 触发中断
  • 运行一下,看看最终效果

实验二

  • 实验目标:处理外部时钟中断(接主 8259A IRQ0 引脚),接收到时钟中断后,在屏幕上循环打印 '0'-'9'
  • 先提供实验完整代码:loader.asm
  • 实现思路跟实验一一样,都是套路
  • 由于本实验依赖 8259A,所以先把 8259A驱动编写 中实现的驱动代码复制过来并初始化 8259A

call pic_init

  • 准备中断服务函数,注意程序最后需要调用 write_m_EOI 手动结束中断,如果不手动结束中断,那么该中断只会触发一次
TimerHandler:
    cmp al, '9'         ; 判断 al 是否为 '9'
    je .to_0            ; 如果 al=9,则跳转到 .to_0 处执行
    inc al
    jmp .display        ; 跳转到 .display 处执行
.to_0:
    mov al, '0'         ; al = '0'
.display:               ; 使用现存方式打印,al 为要打印的字符,ah 为打印属性
    mov ah, 0x0F        ; 打印属性,黑底白字
    mov [gs:(80*4+0)*2], ax ; (80 * 4 + 0)*2 ; 坐标 (0, 4)
    call write_m_EOI    ; 手动结束中断
    iret
TimerHandler_Offset   equ     TimerHandler-CODE32_START
  • 回想一下8259A驱动编写 中,pic_init 将主 8259A IRQ0 引脚(外部时钟)的中断向量号设置为了
• 0x20,于是我们在中段描述符表 IDT 中添加对应的中断描述符(第 33 个)
...
Gate   CODE32_SELECTOR,  TimerHandler_Offset,  0,  DA_INTR_GATE
...
  • 编译运行一下,理论是应看到实验现象,但是并没有
  • 思考一下,8259A 的 IMR 寄存器起到放行作用,是不是 IMR 寄存器默认不放行呢?
  • 于是,我们把 IMR 寄存器的 bit0 (对应IRQ0,即外部时钟引脚)清零
EnableTimer:
    push ax
    call read_m_IMR
    and al, 0xFE
    call write_m_IMR
    pop ax
    ret
  • 再次编译运行一下,发现还是不行,又是什么原因导致没有出现我们想要的现象呢?
  • 我们又想到, 在讲 8259A 的时候我们就说过,不过 8259A 能屏蔽中断,CPU 也可以屏蔽中断,是不是 CPU 把外部中断屏蔽了呢?
  • 实验一下不就知道了吗,sti 指令就可以开启 CPU 中断(cli:关中断)
  • 果然,加上 sti 指令后,屏幕上果然循环打印出 '0'-'9'。这说明 CPU 默认是屏蔽外部中断的
  • 再改动一下,不执行 EnableTimer 函数,即不手动放行 8259A IRQ 中断

; call EnableTimer

sti

  • 发现依然能循环打印出 '0'-'9',这说明 8259A 的 IMR 寄存器是默认放行的
  • 最后贴上实验截图,虽然无法看到 '0'-'9' 跳动过程,但至少能证明我们的代码是实实在在运行成功的

中断嵌套

  • 提到中断,那么我们就得思考一个问题,那就是中断嵌套。什么是中断嵌套呢?
  • 比如我们上面实验二所做的外部时钟中断,假设时钟中断引脚 IRQ0 每 5ms 触发一次,而对应的中断服务程序执行实时间是 20ms。这时候就会遇到一个问题,在第一次中断服务程序正在执行的过程中,又触发了一次中断,这种情况我们就称之为中断嵌套
  • 从 pic_init 初始化主 8259A 函数中 ICW4 寄存器的设置中,我们知道,主 8259A 被设置成了特殊全嵌套模式,即中断服务程序执行过程中,仍然响应同级中断
  • 对于外部中断,不光 8259A 的 IMR 寄存器提供了中断屏蔽机制, CPU 也有个总开关(eflags 的 IF 位),也可以屏蔽中断
  • 就拿实验二的代码,让我们反汇编后断点调试,看一看中断执行前后 IF 位的情况吧。IF=1:可以响应外部中断;IF=0:屏蔽外部中断
  • make 之后反汇编一下

ndisasm -o 0x900 loader.bin  > loader.txt

  • 查看 loader.txt 文件,找到开启中断 sti 指令地址:0x1214,TimerHandler 函数入口处(cmp al, '9')地址:0x125B,TimerHandler 函数最后返回指令 iret 地址:0x1273,使用 reg 指令,看一下各状态下 eflags 中 IF 位的变化(大写表示值为 1, 小写表示值为 0)
<bochs:1> b 0x1214
<bochs:2> b 0x125B
<bochs:3> b 0x1273
<bochs:4> c
...
(0) Breakpoint 1, 0x00001214 in ?? ()
Next at t=16768062
(0) [0x00001214] 0008:00000032 (unk. ctxt): sti                       ; fb
<bochs:5> reg
eax: 0x00000030 48
ecx: 0x00000009 9
edx: 0x00000300 768
ebx: 0x0000000f 15
esp: 0x00000fff 4095
ebp: 0x00000011 17
esi: 0x0000130f 4879
edi: 0x00000923 2339
eip: 0x00000032
eflags 0x00000046: id vip vif ac vm rf nt IOPL=0 of df if tf sf ZF af PF cf
<bochs:6> s
Next at t=16768063
(0) [0x00001215] 0008:00000033 (unk. ctxt): jmp .-2 (0x00001215)      ; ebfe
<bochs:7> reg
eax: 0x00000030 48
ecx: 0x00000009 9
edx: 0x00000300 768
ebx: 0x0000000f 15
esp: 0x00000fff 4095
ebp: 0x00000011 17
esi: 0x0000130f 4879
edi: 0x00000923 2339
eip: 0x00000033
eflags 0x00000246: id vip vif ac vm rf nt IOPL=0 of df IF tf sf ZF af PF cf
<bochs:8> c
(0) Breakpoint 3, 0x00001273 in ?? ()
Next at t=16921763
(0) [0x00001273] 0008:00000091 (unk. ctxt): iretd                     ; cf
<bochs:9> reg
eax: 0x00000f31 3889
ecx: 0x00000009 9
edx: 0x00000300 768
ebx: 0x0000000f 15
esp: 0x00000ff3 4083
ebp: 0x00000011 17
esi: 0x0000130f 4879
edi: 0x00000923 2339
eip: 0x00000091
eflags 0x00000003: id vip vif ac vm rf nt IOPL=0 of df if tf sf zf af pf CF
<bochs:10> s
Next at t=16921764
(0) [0x00001215] 0008:00000033 (unk. ctxt): jmp .-2 (0x00001215)      ; ebfe
<bochs:11> reg
eax: 0x00000f31 3889
ecx: 0x00000009 9
edx: 0x00000300 768
ebx: 0x0000000f 15
esp: 0x00000fff 4095
ebp: 0x00000011 17
esi: 0x0000130f 4879
edi: 0x00000923 2339
eip: 0x00000033
eflags 0x00000246: id vip vif ac vm rf nt IOPL=0 of df IF tf sf ZF af PF cf
<bochs:12>
  • 从调试信息上看
  • sti 指令的本质就是将 eflags IF 位置 1
  • 在中断服务程序 TimerHandler 执行期间, IF 位为 0,说明 CPU 在中断服务程序执行过程中本身是不响应其它中断的
  • 如果你想实现在中断服务程序执行过程中依旧能响应其它中断,可以在中断服务程序的开始处手动加上 sti 指令


相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
目录
相关文章
|
消息中间件 数据可视化 关系型数据库
(3)sparkstreaming从kafka接入实时数据流最终实现数据可视化展示
1)我们通过kafka与各个业务系统的数据对接,将各系统中的数据实时接到kafka; 2)通过sparkstreaming接入kafka数据流,定义时间窗口和计算窗口大小,业务计算逻辑处理; 3)将结果数据写入到mysql; 4)通过可视化平台接入mysql数据库,这里使用的是NBI大数据可视化构建平台; 5)在平台上通过拖拽式构建各种数据应用,数据展示;
(3)sparkstreaming从kafka接入实时数据流最终实现数据可视化展示
|
7月前
|
机器学习/深度学习 人工智能 并行计算
YOLOv11改进策略【YOLO和Mamba】| MLLA:Mamba-Like Linear Attention,融合Mamba设计优势的注意力机制
YOLOv11改进策略【YOLO和Mamba】| MLLA:Mamba-Like Linear Attention,融合Mamba设计优势的注意力机制
542 9
|
9月前
|
人工智能 前端开发 算法
主动式智能导购 AI 助手构建方案评测
《主动式智能导购 AI 助手构建方案评测》详细评估了该方案在部署体验、技术原理理解及生产环境应用指导等方面的表现。方案在智能导购领域展现出一定潜力,但文档的详细程度和技术细节的阐述仍有改进空间,特别是在复杂操作和高级功能的指导上。总体而言,该方案具备优势,但需进一步优化以更好地满足企业需求。
197 10
|
机器学习/深度学习 SQL 存储
机器学习PAI常见问题之资源不足如何解决
PAI(平台为智能,Platform for Artificial Intelligence)是阿里云提供的一个全面的人工智能开发平台,旨在为开发者提供机器学习、深度学习等人工智能技术的模型训练、优化和部署服务。以下是PAI平台使用中的一些常见问题及其答案汇总,帮助用户解决在使用过程中遇到的问题。
|
10月前
|
开发框架 开发者 UED
ArkUI常用布局:构建响应式和高效的用户界面
本文详细介绍了HarmonyOS应用开发中ArkUI框架的常用布局方式,包括线性布局、层叠布局、弹性布局、相对布局、栅格布局、列表和轮播布局。每种布局方式都配有示例代码,帮助开发者构建响应式和高效的用户界面。通过合理选择和使用这些布局,可以显著提升应用的性能和用户体验。
400 0
|
物联网 SDN 网络虚拟化
VXLAN:彻底改变网络虚拟化
【7月更文挑战第3天】
870 0
VXLAN:彻底改变网络虚拟化
|
NoSQL Java MongoDB
如何在Spring Boot应用中集成MongoDB数据库
如何在Spring Boot应用中集成MongoDB数据库
|
存储 边缘计算 人工智能
云计算:现代计算环境的关键力量
云计算是基于互联网的计算模式,提供IaaS、PaaS和SaaS服务,改变计算资源获取方式,赋予企业灵活、可扩展的资源调配。其优势包括可扩展性、成本效益、灵活性和效率提升,但也面临安全性、依赖性等挑战。未来趋势涉及边缘计算、混合云、AI融合以及法规遵从与数据安全的强化。企业应适应云计算发展,制定相应策略。
|
图形学
Unity——拖尾特效
Unity——拖尾特效
757 0
|
机器学习/深度学习 计算机视觉
【YOLOv8改进-论文笔记】SCConv :即插即用的空间和通道重建卷积
该文介绍了一种针对卷积神经网络(CNN)的改进方法,名为SCConv,旨在减少计算冗余并提升特征学习效率。SCConv包含空间重构单元(SRU)和通道重构单元(CRU),分别处理空间和通道冗余。SRU利用分离-重构策略抑制空间冗余,而CRU通过分割-变换-融合策略减少通道冗余。SCConv可直接插入现有CNN架构中,实验结果显示,整合SCConv的模型能在降低复杂性和计算成本的同时保持或提高性能。此外,文章还展示了如何在YOLOv8中应用SCConv。