操作系统真象还原第7章:中断

2023-10-20 01:10

本文主要是介绍操作系统真象还原第7章:中断,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

这一章挺琐碎的,看了好几遍,好好捋一捋这一章干的事情。
这一章主要就是利用可编程中断控制器8259A来设置中断处理程序,其中32个中断是计算机自己有的(从0开始数),这一章目前只开启了时钟中断,所以这里设置的中断向量号从32开始,然后有个中断描述符表(IDT),类似与全局描述符表(GDT),中断向量号就类似与索引,从IDT中找到中断描述符,中断描述符中包括选择子(毕竟最终要找到中断处理程序,而中断处理程序肯定是在某个内存地址上的),属性,以及偏移地址,其中选择子在GDT中找到段描述符,段描述中存储着虚拟地址,然后经过页表映射到的地址+偏移地址即为中断处理程序的入口地址。
其中中断是分为三种的,故障,陷阱,abort(终止),故障是最轻的那种,相当与失败了可以返回到原程序,陷阱就是可以内嵌中断(就是信号机制),终止就是最坏的那种中断,出现了最大的故障,挽救不会来。关于8259A需要利用端口读写来设置中断机制,包括可屏蔽啥的
关于特权级,主要是对于cpu来说,有0-3级别,级别越小,特权级限越高,访问权限也就越高。对于代码段来说,也就是cs:ip执行的那些段,除了中断之外只允许平级的特权级之间的代码段切换,而对于访问数据段则必须要当前特权级小于数据段的特权级,也就是当前的访问权限大于数据段的访问权限。
在这里插入图片描述

代码

interrupt.c

#include "interrupt.h"
#include "stdint.h"
#include "global.h"
#include "io.h"
#include "print.h"#define PIC_M_CTRL 0x20	       // 这里用的可编程中断控制器是8259A,主片的控制端口是0x20
#define PIC_M_DATA 0x21	       // 主片的数据端口是0x21
#define PIC_S_CTRL 0xa0	       // 从片的控制端口是0xa0
#define PIC_S_DATA 0xa1	       // 从片的数据端口是0xa1#define IDT_DESC_CNT 0x21      // 目前总共支持的中断数/*中断门描述符结构体*/
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);
static struct gate_desc idt[IDT_DESC_CNT];   // idt是中断描述符表,本质上就是个中断门描述符数组char* intr_name[IDT_DESC_CNT];		     // 用于保存异常的名字
intr_handler idt_table[IDT_DESC_CNT];	     // 定义中断处理程序数组.在kernel.S中定义的intrXXentry只是中断处理程序的入口,最终调用的是ide_table中的处理程序
extern intr_handler intr_entry_table[IDT_DESC_CNT];	    // 声明引用定义在kernel.S中的中断处理函数入口数组/* 初始化可编程中断控制器8259A */
static void pic_init(void) {/* 初始化主片 */outb (PIC_M_CTRL, 0x11);   // ICW1: 边沿触发,级联8259, 需要ICW4.outb (PIC_M_DATA, 0x20);   // ICW2: 起始中断向量号为0x20,也就是IR[0-7] 为 0x20 ~ 0x27.outb (PIC_M_DATA, 0x04);   // ICW3: IR2接从片. outb (PIC_M_DATA, 0x01);   // ICW4: 8086模式, 正常EOI/* 初始化从片 */outb (PIC_S_CTRL, 0x11);    // ICW1: 边沿触发,级联8259, 需要ICW4.outb (PIC_S_DATA, 0x28);    // ICW2: 起始中断向量号为0x28,也就是IR[8-15] 为 0x28 ~ 0x2F.outb (PIC_S_DATA, 0x02);    // ICW3: 设置从片连接到主片的IR2引脚outb (PIC_S_DATA, 0x01);    // ICW4: 8086模式, 正常EOI/* 打开主片上IR0,也就是目前只接受时钟产生的中断 */outb (PIC_M_DATA, 0xfe);outb (PIC_S_DATA, 0xff);put_str("   pic_init done\n");
}/* 创建中断门描述符 */
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;for (i = 0; i < IDT_DESC_CNT; i++) {make_idt_desc(&idt[i], IDT_DESC_ATTR_DPL0, intr_entry_table[i]); }put_str("   idt_desc_init done\n");
}/* 通用的中断处理函数,一般用在异常出现时的处理 */
static void general_intr_handler(uint8_t vec_nr) {if (vec_nr == 0x27 || vec_nr == 0x2f) {	// 0x2f是从片8259A上的最后一个irq引脚,保留return;		//IRQ7和IRQ15会产生伪中断(spurious interrupt),无须处理。}put_str("int vector: 0x");put_int(vec_nr);put_char('\n');
}/* 完成一般中断处理函数注册及异常名称注册 */
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";}/*完成有关中断的所有初始化工作*/
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");
}

timer.c

#include "timer.h"
#include "io.h"
#include "print.h"#define IRQ0_FREQUENCY	   100
#define INPUT_FREQUENCY	   1193180
#define COUNTER0_VALUE	   INPUT_FREQUENCY / IRQ0_FREQUENCY
#define CONTRER0_PORT	   0x40
#define COUNTER0_NO	   0
#define COUNTER_MODE	   2
#define READ_WRITE_LATCH   3
#define PIT_CONTROL_PORT   0x43/* 把操作的计数器counter_no、读写锁属性rwl、计数器模式counter_mode写入模式控制寄存器并赋予初始值counter_value */
static void frequency_set(uint8_t counter_port, \uint8_t counter_no, \uint8_t rwl, \uint8_t counter_mode, \uint16_t counter_value) {
/* 往控制字寄存器端口0x43中写入控制字 */outb(PIT_CONTROL_PORT, (uint8_t)(counter_no << 6 | rwl << 4 | counter_mode << 1));
/* 先写入counter_value的低8位 */outb(counter_port, (uint8_t)counter_value);
/* 再写入counter_value的高8位 */outb(counter_port, (uint8_t)counter_value >> 8);
}/* 初始化PIT8253 */
void timer_init() {put_str("timer_init start\n");/* 设置8253的定时周期,也就是发中断的周期 */frequency_set(CONTRER0_PORT, COUNTER0_NO, READ_WRITE_LATCH, COUNTER_MODE, COUNTER0_VALUE);put_str("timer_init done\n");
}

init.c

#include "init.h"
#include "print.h"
#include "interrupt.h"
#include "../device/timer.h"		  // 用相对路径演示头文件包含/*负责初始化所有模块 */
void init_all() {put_str("init_all\n");idt_init();    // 初始化中断timer_init();  // 初始化PIT
}

global.h

#ifndef __KERNEL_GLOBAL_H
#define __KERNEL_GLOBAL_H
#include "stdint.h"#define	 RPL0  0
#define	 RPL1  1
#define	 RPL2  2
#define	 RPL3  3#define TI_GDT 0
#define TI_LDT 1#define SELECTOR_K_CODE	   ((1 << 3) + (TI_GDT << 2) + RPL0)
#define SELECTOR_K_DATA	   ((2 << 3) + (TI_GDT << 2) + RPL0)
#define SELECTOR_K_STACK   SELECTOR_K_DATA 
#define SELECTOR_K_GS	   ((3 << 3) + (TI_GDT << 2) + RPL0)//--------------   IDT描述符属性  ------------
#define	 IDT_DESC_P	 1 
#define	 IDT_DESC_DPL0   0
#define	 IDT_DESC_DPL3   3
#define	 IDT_DESC_32_TYPE     0xE   // 32位的门
#define	 IDT_DESC_16_TYPE     0x6   // 16位的门,不用,定义它只为和32位门区分
#define	 IDT_DESC_ATTR_DPL0  ((IDT_DESC_P << 7) + (IDT_DESC_DPL0 << 5) + IDT_DESC_32_TYPE)
#define	 IDT_DESC_ATTR_DPL3  ((IDT_DESC_P << 7) + (IDT_DESC_DPL3 << 5) + IDT_DESC_32_TYPE)#endif

kernel.S

[bits 32]
%define ERROR_CODE nop		 ; 若在相关的异常中cpu已经自动压入了错误码,为保持栈中格式统一,这里不做操作.
%define ZERO push 0		 ; 若在相关的异常中cpu没有压入错误码,为了统一栈中格式,就手工压入一个0extern idt_table		 ;idt_table是C中注册的中断处理程序数组isection .data
global intr_entry_table
intr_entry_table:%macro VECTOR 2
section .text
intr%1entry:		 ; 每个中断处理程序都要压入中断向量号,所以一个中断类型一个中断处理程序,自己知道自己的中断向量号是多少%2				 ; 中断若有错误码会压在eip后面 
; 以下是保存上下文环境push dspush espush fspush gspushad			 ; PUSHAD指令压入32位寄存器,其入栈顺序是: EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI; 如果是从片上进入的中断,除了往从片上发送EOI外,还要往主片上发送EOI mov al,0x20                   ; 中断结束命令EOIout 0xa0,al                   ; 向从片发送out 0x20,al                   ; 向主片发送push %1			 ; 不管idt_table中的目标程序是否需要参数,都一律压入中断向量号,调试时很方便call [idt_table + %1*4]       ; 调用idt_table中的C版本中断处理函数jmp intr_exitsection .datadd    intr%1entry	 ; 存储各个中断入口程序的地址,形成intr_entry_table数组
%endmacrosection .text
global intr_exit
intr_exit:	     
; 以下是恢复上下文环境add esp, 4			   ; 跳过中断号popadpop gspop fspop espop dsadd esp, 4			   ; 跳过error_codeiretdVECTOR 0x00,ZERO
VECTOR 0x01,ZERO
VECTOR 0x02,ZERO
VECTOR 0x03,ZERO 
VECTOR 0x04,ZERO
VECTOR 0x05,ZERO
VECTOR 0x06,ZERO
VECTOR 0x07,ZERO 
VECTOR 0x08,ERROR_CODE
VECTOR 0x09,ZERO
VECTOR 0x0a,ERROR_CODE
VECTOR 0x0b,ERROR_CODE 
VECTOR 0x0c,ZERO
VECTOR 0x0d,ERROR_CODE
VECTOR 0x0e,ERROR_CODE
VECTOR 0x0f,ZERO 
VECTOR 0x10,ZERO
VECTOR 0x11,ERROR_CODE
VECTOR 0x12,ZERO
VECTOR 0x13,ZERO 
VECTOR 0x14,ZERO
VECTOR 0x15,ZERO
VECTOR 0x16,ZERO
VECTOR 0x17,ZERO 
VECTOR 0x18,ERROR_CODE
VECTOR 0x19,ZERO
VECTOR 0x1a,ERROR_CODE
VECTOR 0x1b,ERROR_CODE 
VECTOR 0x1c,ZERO
VECTOR 0x1d,ERROR_CODE
VECTOR 0x1e,ERROR_CODE
VECTOR 0x1f,ZERO 
VECTOR 0x20,ZERO

结果

在这里插入图片描述

这篇关于操作系统真象还原第7章:中断的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/243604

相关文章

【Linux进阶】UNIX体系结构分解——操作系统,内核,shell

1.什么是操作系统? 从严格意义上说,可将操作系统定义为一种软件,它控制计算机硬件资源,提供程序运行环境。我们通常将这种软件称为内核(kerel),因为它相对较小,而且位于环境的核心。  从广义上说,操作系统包括了内核和一些其他软件,这些软件使得计算机能够发挥作用,并使计算机具有自己的特生。这里所说的其他软件包括系统实用程序(system utility)、应用程序、shell以及公用函数库等

【操作系统】信号Signal超详解|捕捉函数

🔥博客主页: 我要成为C++领域大神🎥系列专栏:【C++核心编程】 【计算机网络】 【Linux编程】 【操作系统】 ❤️感谢大家点赞👍收藏⭐评论✍️ 本博客致力于知识分享,与更多的人进行学习交流 ​ 如何触发信号 信号是Linux下的经典技术,一般操作系统利用信号杀死违规进程,典型进程干预手段,信号除了杀死进程外也可以挂起进程 kill -l 查看系统支持的信号

操作系统实训复习笔记(1)

目录 Linux vi/vim编辑器(简单) (1)vi/vim基本用法。 (2)vi/vim基础操作。 进程基础操作(简单) (1)fork()函数。 写文件系统函数(中等) ​编辑 (1)C语言读取文件。 (2)C语言写入文件。 1、write()函数。  读文件系统函数(简单) (1)read()函数。 作者本人的操作系统实训复习笔记 Linux

SQL Server中,用Restore DataBase把数据库还原到指定的路径

restore database 数据库名 from disk='备份文件路径' with move '数据库文件名' to '数据库文件放置路径', move '日志文件名' to '日志文件存放置路径' Go 如: restore database EaseWe from disk='H:\EaseWe.bak' with move 'Ease

HarmonyOS NEXT:华为开启全新操作系统时代

在全球科技浪潮的汹涌澎湃中,华为再次以创新者的姿态,引领了一场关于操作系统的革命。HarmonyOS NEXT,这一由华为倾力打造的分布式操作系统,不仅是对现有技术的一次大胆突破,更是对未来智能生活的一次深邃展望。 HarmonyOS NEXT并非简单的迭代升级,而是在华为多年技术积淀的基础上,对操作系统的一次彻底重构。它采用微内核架构,摒弃了传统的宏内核模式,实现了模块化和组件化的设计理念

Linux操作系统段式存储管理、 段页式存储管理

1、段式存储管理 1.1分段 进程的地址空间:按照程序自身的逻辑关系划分为若干个段,每个段都有一个段名(在低级语言中,程序员使用段名来编程),每段从0开始编址。内存分配规则:以段为单位进行分配,每个段在内存中占连续空间,但各段之间可以不相邻。 分段系统的逻辑地址结构由段号(段名)和段内地址(段内偏移量)所组成。 1.2段表 每一个程序设置一个段表,放在内存,属于进程的现场信息

【Linux详解】冯诺依曼架构 | 操作系统设计 | 斯坦福经典项目Pintos

目录 一. 冯诺依曼体系结构 (Von Neumann Architecture) 注意事项 存储器的意义:缓冲 数据流动示例 二. 操作系统 (Operating System) 操作系统的概念 操作系统的定位与目的 操作系统的管理 系统调用和库函数 操作系统的管理: sum 三. 系统调用实现示例:Pintos 项目 Step 1:进入 examples 目录 St

【操作系统】第五章 文件系统

文件系统 5.1 概述5.2 文件5.2.1 文件及文件系统的概念5.2.2 文件的属性5.2.3 文件的操作5.2.4 文件的类型文件分类 5.3 文件的访问文件的逻辑结构顺序文件顺序文件的优缺点 索引文件索引顺序文件直接文件和哈希文件 5.4 文件保护5.5 文件目录文件控制块(FCB)文件控制块的内容 文件目录索引结点磁盘索引结点内存索引结点 单级目录结构二级目录结构多级目录结构

Linux|操作系统运维|磁盘性能检测之fio和iostat的初步使用

前言: 有的时候,我们接手一个新的服务器的时候,需要了解该服务器的磁盘性能是否可靠,比如,磁盘是否有坏道,磁盘的读写性能是否能够符合我们将要部署的服务,例如数据库服务,如果该数据库是一个读写比较频繁也就是IO比较高的数据库,那么,该磁盘是否能够支持高IO呢? 针对以上需求,建议使用工具fio和iostat这两个工具 一、 iostat在centos7下的安装 配置update源即可 i

软件设计师笔记-操作系统知识

操作系统的作用 操作系统(Operating System, OS)在计算机系统中扮演着至关重要的角色。通过资源管理提高计算机系统的效率;改善人机界面,向用户提供友好的工作环境。 通过资源管理提高计算机系统的效率: 处理器管理:操作系统负责管理和调度计算机的中央处理器(CPU)资源。它使用各种调度算法(如先来先服务、最短作业优先、优先级调度等)来确定哪个进程或线程应该在何时获得CPU的使用权