IT教程 ·

【自制操作系统09】中止的代码实现

博客圆美化主题推荐之Slience

因为中断这块的学问和代码都占较大篇幅,因而分红两章来说,上一讲  报告了中断的理论学问,本讲入手下手上代码

一、到现在为止的程序流程图

为了让人人清晰现在的程序进度,画了到现在为止的程序流程图,以下。

【自制操作系统09】中止的代码实现 IT教程 第1张

右半部份的时序图,就是我们本日要做做的事变,实在一句话就是:初始化中断描述符表,个中中断例程非常简朴,只是简朴地将中断向量号输出在屏幕上

二、先上代码

重要代码

【自制操作系统09】中止的代码实现 IT教程 第2张

1 #include "print.h"
2 #include "init.h"
3 void main(void){
4     put_str("I am kerneln");
5     init_all();
6     asm volatile("sti");
7     while(1);
8 }

main.c
【自制操作系统09】中止的代码实现 IT教程 第2张

1 #include "init.h"
2 #include "print.h"
3 #include "interrupt.h"
4 
5 // 担任初始化一切模块
6 void init_all() {
7     put_str("init_alln");
8     idt_init();
9 }

init.c
【自制操作系统09】中止的代码实现 IT教程 第2张

  1 #include "interrupt.h"
  2 #include "stdint.h"
  3 #include "global.h"
  4 #include "io.h"
  5 
  6 
  7 #define PIC_M_CTRL 0x20 //主片掌握端口
  8 #define PIC_M_DATA 0x21 //主片数据端口
  9 #define PIC_S_CTRL 0xa0 //从片掌握端口
 10 #define PIC_S_DATA 0xa1 //从片数据端口
 11 
 12 #define IDT_DESC_CNT 0x21    //现在统共支撑的中断数
 13 
 14 // 中断门描述符构造体
 15 struct gate_desc{
 16     uint16_t func_offset_low_word;
 17     uint16_t selector;
 18     uint8_t  dcount;
 19     uint8_t  attribute;
 20     uint16_t func_offset_high_word;
 21 };
 22 
 23 // 静态函数声明,非必需
 24 static void make_idt_desc(struct gate_desc* p_gdesc, uint8_t attr, intr_handler function);
 25 // 中断门描述符表的数组
 26 static struct gate_desc idt[IDT_DESC_CNT];
 27 // 用于保存非常名
 28 char* intr_name[IDT_DESC_CNT];
 29 // 定义中断处置惩罚程序数组,在kernel.asm中定义的intrXXentry。只是中断处置惩罚程序的进口,终究挪用idt_table中的处置惩罚程序
 30 intr_handler idt_table[IDT_DESC_CNT];
 31 // 声明援用定义在kernel.asm中的中断处置惩罚函数进口数组
 32 extern intr_handler intr_entry_table[IDT_DESC_CNT];
 33 // 初始化可编程中断掌握器 8259A
 34 static void pic_init(void) {
 35 
 36     /*初始化主片 */
 37     outb (PIC_M_CTRL, 0x11); // ICW1: 边缘触发,级联8259, 须要ICW4
 38     outb (PIC_M_DATA, 0x20); // ICW2: 肇端中断向量号为0x20, 也就是IR[0-7] 为 0x20 ~ 0x27
 39     outb (PIC_M_DATA, 0x04); // ICW3: IR2 接从片
 40     outb (PIC_M_DATA, 0x01); // ICW4: 8086 形式, 平常EOI
 41     
 42     /*初始化从片 */
 43     outb (PIC_S_CTRL, 0x11); // ICW1: 边缘触发,级联8259, 须要ICW4
 44     outb (PIC_S_DATA, 0x28); // ICW2: 肇端中断向量号为0x28, 也就是IR[8-15]为0x28 ~ 0x2F
 45     outb (PIC_S_DATA, 0x02); // ICW3: 设置从片连接到主片的IR2 引脚
 46     outb (PIC_S_DATA, 0x01); // ICW4: 8086 形式, 平常EOI
 47     
 48     /*翻开主片上IR0,也就是现在只接收时钟发生的中断 */
 49     outb (PIC_M_DATA, 0xfe);
 50     outb (PIC_S_DATA, 0xff);
 51     
 52     put_str("   pic_init donen");
 53 }
 54 
 55 //建立中断门描述符
 56 static void make_idt_desc(struct gate_desc* p_gdesc, uint8_t attr, intr_handler function) {
 57     p_gdesc->func_offset_low_word = (uint32_t)function & 0x0000FFFF;
 58     p_gdesc->selector = SELECTOR_K_CODE;
 59     p_gdesc->dcount = 0;
 60     p_gdesc->attribute = attr;
 61     p_gdesc->func_offset_high_word = ((uint32_t)function & 0xFFFF0000) >> 16;
 62 }
 63 
 64 // 初始化中断描述符表
 65 static void idt_desc_init(void) {
 66     int i;
 67     for(i = 0; i < IDT_DESC_CNT; i++) {
 68         make_idt_desc(&idt[i], IDT_DESC_ATTR_DPL0, intr_entry_table[i]);
 69     }
 70     put_str("   idt_desc_init donen");
 71 }
 72 
 73 // 通用的中断处置惩罚函数,平常用在非常出现时的处置惩罚
 74 static void general_intr_handler(uint8_t vec_nr) {
 75     if(vec_nr == 0x27 || vec_nr == 0x2f) {
 76         return;
 77     }
 78     put_str("int vector:0x");
 79     put_int(vec_nr);
 80     put_char('n');
 81 }
 82 
 83 // 完成平常中断处置惩罚函数注册及非常称号注册
 84 static void exception_init(void) {
 85     int i;
 86     for(i = 0; i < IDT_DESC_CNT; i++) {
 87         // 默以为这个,今后会由 register_handler 来注册细致处置惩罚函数
 88         idt_table[i] = general_intr_handler;
 89         intr_name[i] = "unknown";
 90     }
 91     intr_name[0] = "#DE Divide Error"; 
 92     intr_name[1] = "#DB Debug Exception"; 
 93     intr_name[2] = "NMI Interrupt"; 
 94     intr_name[3] = "#BP Breakpoint Exception"; 
 95     intr_name[4] = "#OF Overflow Exception"; 
 96     intr_name[5] = "#BR BOUND Range Exceeded Exception"; 
 97     intr_name[6] = "#UD Invalid Opcode Exception"; 
 98     intr_name[7] = "#NM Device Not Available Exception"; 
 99     intr_name[8] = "#DF Double Fault Exception"; 
100     intr_name[9] = "Coprocessor Segment Overrun"; 
101     intr_name[10] = "#TS Invalid TSS Exception"; 
102     intr_name[11] = "#NP Segment Not Present"; 
103     intr_name[12] = "#SS Stack Fault Exception"; 
104     intr_name[13] = "#GP General Protection Exception"; 
105     intr_name[14] = "#PF Page-Fault Exception"; 
106     // intr_name[15] 第 15 项是 intel 保存项,未运用
107     intr_name[16] = "#MF x87 FPU Floating-Point Error"; 
108     intr_name[17] = "#AC Alignment Check Exception"; 
109     intr_name[18] = "#MC Machine-Check Exception"; 
110     intr_name[19] = "#XF SIMD Floating-Point Exception";
111 }
112 
113 
114 // 完成有关中断到一切初始化事情
115 void idt_init() {
116     put_str("idt_init startn");
117     idt_desc_init();    // 初始化中断描述符表
118     exception_init();    // 初始化通用中断处置惩罚函数
119     pic_init();        // 初始化8259A
120     
121     // 加载idt
122     uint64_t idt_operand = ((sizeof(idt) - 1) | ((uint64_t)((uint32_t)idt << 16)));
123     asm volatile("lidt %0" : : "m" (idt_operand));
124     put_str("idt_init donen");
125 }

interrupt.c
【自制操作系统09】中止的代码实现 IT教程 第2张

 1 [bits 32]
 2 %define ERROR_CODE nop
 3 %define ZERO push 0
 4 
 5 extern idt_table
 6 
 7 section .data
 8 global intr_entry_table
 9 intr_entry_table:
10 
11 %macro VECTOR 2
12     section .text
13     intr%1entry:
14         %2
15         push ds
16         push es
17         push fs
18         push gs
19         pushad
20         
21         ;假如是从片上进入到中断,除了往从片上发送EOI外,还要往主片上发送EOI
22         mov al,0x20
23         out 0xa0,al
24         out 0x20,al
25         
26         push %1
27         call [idt_table + %1*4]
28         jmp intr_exit
29         
30     section .data
31         dd intr%1entry
32 %endmacro
33 
34 section .text
35 global intr_exit
36 intr_exit:
37     add esp,4
38     popad
39     pop gs
40     pop fs
41     pop es
42     pop ds
43     add esp,4
44     iretd
45 
46 VECTOR 0X00,ZERO
47 VECTOR 0X01,ZERO
48 VECTOR 0X02,ZERO
49 VECTOR 0X03,ZERO
50 VECTOR 0X04,ZERO
51 VECTOR 0X05,ZERO
52 VECTOR 0X06,ZERO
53 VECTOR 0X07,ZERO
54 VECTOR 0X08,ZERO
55 VECTOR 0X09,ZERO
56 VECTOR 0X0a,ZERO
57 VECTOR 0X0b,ZERO
58 VECTOR 0X0c,ZERO
59 VECTOR 0X0d,ZERO
60 VECTOR 0X0e,ZERO
61 VECTOR 0X0f,ZERO
62 VECTOR 0X10,ZERO
63 VECTOR 0X11,ZERO
64 VECTOR 0X12,ZERO
65 VECTOR 0X13,ZERO
66 VECTOR 0X14,ZERO
67 VECTOR 0X15,ZERO
68 VECTOR 0X16,ZERO
69 VECTOR 0X17,ZERO
70 VECTOR 0X18,ZERO
71 VECTOR 0X19,ZERO
72 VECTOR 0X1a,ZERO
73 VECTOR 0X1b,ZERO
74 VECTOR 0X1c,ZERO
75 VECTOR 0X1d,ZERO
76 VECTOR 0X1e,ERROR_CODE
77 VECTOR 0X1f,ZERO
78 VECTOR 0X20,ZERO

kernel.asm
【自制操作系统09】中止的代码实现 IT教程 第2张
Makefile

头文件

【自制操作系统09】中止的代码实现 IT教程 第2张

 1 #ifndef __KERNEL_GLOBAL_H
 2 #define __KERNEL_GLOBAL_H
 3 #include "stdint.h"
 4 
 5 #define RPL0 0
 6 #define RPL1 1
 7 #define RPL2 2
 8 #define RPL3 3
 9 
10 #define TI_GDT 0
11 #define TI_LDT 1
12 
13 #define SELECTOR_K_CODE ((1 << 3) + (TI_GDT << 2) + RPL0)
14 #define SELECTOR_K_DATA ((2 << 3) + (TI_GDT << 2) + RPL0)
15 #define SELECTOR_K_STACK SELECTOR_K_DATA
16 #define SELECTOR_K_GS ((3 << 3) + (TI_GDT << 2) + RPL0)
17 
18 /*-------------- IDT 描述符属性 ------------*/
19 #define IDT_DESC_P 1
20 #define IDT_DESC_DPL0 0
21 #define IDT_DESC_DPL3 3
22 #define IDT_DESC_32_TYPE 0xE // 32 位的门
23 #define IDT_DESC_16_TYPE 0x6 // 16 位的门,不会用到,定义它只为和32 位门辨别
24 #define IDT_DESC_ATTR_DPL0 ((IDT_DESC_P << 7) + (IDT_DESC_DPL0 << 5) + IDT_DESC_32_TYPE)
25 #define IDT_DESC_ATTR_DPL3 ((IDT_DESC_P << 7) + (IDT_DESC_DPL3 << 5) + IDT_DESC_32_TYPE)
26 
27 #endif

global.h
【自制操作系统09】中止的代码实现 IT教程 第2张

1 #ifndef __KERNEL_INIT_H
2 #define __KERNEL_INIT_H
3 void init_all(void);
4 #endif

init.h
【自制操作系统09】中止的代码实现 IT教程 第2张

 1 #ifndef __KERNEL_INTERRUPT_H
 2 #define __KERNEL_INTERRUPT_H
 3 #include "stdint.h"
 4 typedef void* intr_handler;
 5 void idt_init(void);
 6 
 7 /* 定义中断的两种状况:
 8  * INTR_OFF值为0,示意关中断,
 9  * INTR_ON值为1,示意开中断 */
10 enum intr_status {         // 中断状况
11     INTR_OFF,             // 中断封闭
12     INTR_ON                 // 中断翻开
13 };
14 
15 enum intr_status intr_get_status(void);
16 enum intr_status intr_set_status (enum intr_status);
17 enum intr_status intr_enable (void);
18 enum intr_status intr_disable (void);
19 void register_handler(uint8_t vector_no, intr_handler function);
20 #endif

interrupt.h
【自制操作系统09】中止的代码实现 IT教程 第2张

 1 /******************机械形式 *******************
 2 b -- 输出寄存器QImode 称号,即寄存器中的最低8 位:[a-d]l
 3 w -- 输出寄存器HImode 称号,即寄存器中2 个字节的部份,如[a-d]x
 4 
 5 HImode
 6 "Half-Integer"形式,示意一个两字节的整数
 7 QImode
 8 "Quarter-Integer"形式,示意一个一字节的整数
 9 ******************************************************/
10 
11 #ifndef __LIB_IO_H
12 #define __LIB_IO_H
13 #include "stdint.h"
14 
15 /* 向端口port 写入一个字节*/
16 static inline void outb(uint16_t port, uint8_t data) {
17 /*********************************************************
18 对端口指定N 示意0~255, d 示意用dx 存储端口号,
19 %b0 示意对应al,%w1 示意对应dx */
20 asm volatile ( "outb %b0, %w1" : : "a" (data), "Nd" (port));
21 /******************************************************/
22 }
23 
24 /* 将addr 处肇端的word_cnt 个字写入端口port */
25 static inline void outsw(uint16_t port, const void* addr, uint32_t word_cnt) {
26 /*********************************************************
27 +示意此限定即做输入,又做输出.
28 outsw 是把ds:esi 处的16 位的内容写入port 端口,我们在设置段描述符时,
29 已将ds,es,ss 段的挑选子都设置为雷同的值了,此时不必忧郁数据紊乱。 */
30 asm volatile ("cld; rep outsw" : "+S" (addr), "+c" (word_cnt) : "d" (port));
31 /******************************************************/
32 }
33 
34 /* 将从端口port 读入的一个字节返回 */
35 static inline uint8_t inb(uint16_t port) {
36 uint8_t data;
37 asm volatile ("inb %w1, %b0" : "=a" (data) : "Nd" (port));
38 return data;
39 }
40 
41 /* 将从端口port 读入的word_cnt 个字写入addr */
42 static inline void insw(uint16_t port, void* addr, uint32_t word_cnt) {
43 /******************************************************
44 insw 是将从端口port 处读入的16 位内容写入es:edi 指向的内存,
45 我们在设置段描述符时,已将ds,es,ss 段的挑选子都设置为雷同的值了,
46 此时不必忧郁数据紊乱。 */
47 asm volatile ("cld; rep insw" : "+D" (addr), "+c" (word_cnt) : "d" (port) : "memory");
48 /******************************************************/
49 }
50 
51 #endif

io.h

三、代码解读

这段代码也是看的我理解了良久良久,但实在做的事变真的非常简朴,请看下图

【自制操作系统09】中止的代码实现 IT教程 第19张

代码有几个症结的构造,在此列出来

  • gate_desc:中断描述符的构造
  • idt:中断描述符表数组(内里存储的是一个个 64 位的中断描述符,构造就是上述的 gate_desc)
  • intr_entry_table:中断例程进口地点数组(内里存储的是一个个地点,指向一段程序,该程序重要事情就是跳转到 intr_table 表,这内里存的是真正的中断处置惩罚程序,以下)
  • intr_table:中断例程地点数组(内里存储的是一个个地点,指向真正的中断处置惩罚程序)
  • general_intr_handler:通用中断处置惩罚程序的要领
  • register_handler:个性化的中断处置惩罚程序的要领(用来替代上面的 general_intr_handler 要领,本章临时没有)

全部代码纵然完成了如许几件事:

  1. 在内存某位置寄存一个 中断描述符表数组 idt
  2. 该 idt 中的每个中断描述符,让个中断例程地点字段,指向 中断例程进口数组 intr_entry_table 的每个元素
  3. intr_entry_table 的每个元素对应的是一段进口程序,该程序中心代码是 call [intr_table + i * 4],其目标是指向 中断例程地点数组 intr_table 的每个元素
  4. intr_table 的每个元素就是真正的中断程序的代码地点,本讲中的处置惩罚代码 general_intr_handler 仅仅是简朴地打印一个字符串,日后用细致的函数 register_handler 替代
  5. 末了初始化 pic,用 lidt 指令加载中断描述符表 idt,就开启了中断

四、运转

我们看到,时钟中断(向量号为 0x20)进来,延续输出我们中断处置惩罚程序中要输出的内容 “int vector:中断向量号”

【自制操作系统09】中止的代码实现 IT教程 第20张

 

写在末了:开源项目和课程计划

假如你对克己一个操作系统感兴致,无妨追随这个系列课程看下去,以至到场我们,一起来开发。

参考书本

《操作系统原形复原》这本书真的赞!强烈推荐

项目开源

项目开源地点:

当你看到该文章时,代码大概已比文章中的又多写了一些部份了。你能够经由过程提交纪录汗青来检察汗青的代码,我会逐步梳理提交汗青以及项目申明文档,争取给每一课都预备一个可执行的代码。固然文章中的代码也是全的,采纳复制粘贴的体式格局也是完全能够的。

假如你有兴致到场这个克己操作系统的雄师,也能够在留言区留下您的联系体式格局,或许在 gitee 私信我您的联系体式格局。

课程计划

本课程打算出系列课程,我写到哪以为能够写成一篇文章了就写出来分享给人人,终究会完成一个功用周全的操作系统,我以为这是最好的进修操作系统的体式格局了。所以中心碰到的种种坎也会写进去,假如你能延续跟进,随着我一块写,必定会有很好的收货。纵然没有,交个朋侪也是好的哈哈。

现在的系列包含

 

文本相似性热度统计算法实现(一)-整句热度统计

参与评论