中断编程实验

本文涉及的产品
应用型负载均衡 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应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
高可用应用架构
欢迎来到“高可用应用架构”课程,本课程是“弹性计算Clouder系列认证“中的阶段四课程。本课程重点向您阐述了云服务器ECS的高可用部署方案,包含了弹性公网IP和负载均衡的概念及操作,通过本课程的学习您将了解在平时工作中,如何利用负载均衡和多台云服务器组建高可用应用架构,并通过弹性公网IP的方式对外提供稳定的互联网接入,使得您的网站更加稳定的同时可以接受更多人访问,掌握在阿里云上构建企业级大流量网站场景的方法。 学习完本课程后,您将能够: 理解高可用架构的含义并掌握基本实现方法 理解弹性公网IP的概念、功能以及应用场景 理解负载均衡的概念、功能以及应用场景 掌握网站高并发时如何处理的基本思路 完成多台Web服务器的负载均衡,从而实现高可用、高并发流量架构
目录
相关文章
|
7月前
|
物联网 Linux
中断系列第一篇:假装入个门之啥子是中断?
中断系列第一篇:假装入个门之啥子是中断?
69 0
|
Java Linux API
中断-处理程序架构
中断-处理程序架构
154 0
|
Linux
Linux内核分析与应用5-中断
Linux内核分析与应用5-中断
73 0
|
Linux 调度
Linux驱动中断下半部的三种方法
Linux驱动中断下半部的三种方法
|
存储 NoSQL Linux
Linux进程信号(产生、保存、处理)/可重入函数概念/volatile理解/SIGCHLD信号
本篇文章重点详细地写了Linux进程信号的知识点:Linux进程信号的概念、信号产生的方式、信号传递和信号阻塞的原理、信号捕捉的方式、内核态、用户态、可重入函数的概念、volatile理解等等。
Linux进程信号(产生、保存、处理)/可重入函数概念/volatile理解/SIGCHLD信号
|
Linux 调度
Linux驱动开发——中断编程之顶半部与底半部机制(1)
Linux驱动开发——中断编程之顶半部与底半部机制(1)
215 0
Linux驱动开发——中断编程之顶半部与底半部机制(1)
蓝桥杯之单片机学习(七)——中断系统与外部中断应用
蓝桥杯之单片机学习(七)——中断系统与外部中断应用
201 0
蓝桥杯之单片机学习(七)——中断系统与外部中断应用
|
监控 C语言 Perl
西门子S7-1200编程实例,置位/复位指令如何使用?
本节我们来学习置位/复位指令,并通过一个电机起保停控制的实例来学习置位/复位指令如何使用。
西门子S7-1200编程实例,置位/复位指令如何使用?
汇编语言与微机原理实验四:8259单脉冲触发中断
汇编语言与微机原理实验四:8259单脉冲触发中断
173 0
|
C语言 芯片
复习单片机:中断系统(内含1.中断概念+2 中断结构及相关寄存器)(注:相关寄存器是重点)
复习单片机:中断系统(内含1.中断概念+2 中断结构及相关寄存器)(注:相关寄存器是重点)
315 0
复习单片机:中断系统(内含1.中断概念+2 中断结构及相关寄存器)(注:相关寄存器是重点)