从后面的章节开始,orange的书籍上往往没有解释为什么要这样做。我们在学习这段内容的时候,尽量多查找资料,从三个层次思考问题:应该怎么做?为什么要这样做?从本质上来看,这是一个什么问题。
sgdt和lidt分别是store和load GDT的意思,其中cstart改变了gdt_ptr,我们来看一下这个函数
在csstart之中,我们用到了PUBLIC、PRIVATE等修饰符,也用到t_32等数据类型,要知道的是,这个程序现在已经是C语言的程序了。我们在这里已经用int等数据类型,想一想,此刻能不能用printf呢? 我们将打印函数进行封装处理,然后编译。需要注意的是,gcc在处理字符串的时候,会传入函数的是字符串地址,而且字符串的末尾会加上‘/0’,也就是ASCII编码0,这也是为什么dis_str能够找到末尾的原因。
启动和引导:booot.asm 和loader.asm放在目录boot中
库文件:klib.asm && string.asm放在/lib之中
内核:kernel.asm && start.c放在kernel之中
3、makefile
对于全局变量的处理:我们需要全局变量在某个文件中是定义,而在其他文件中仅仅是申明,所以就有一个对extern进行处理的小trick。
如果你对这一部分有一些疑问,那么或许你应该回忆一下中断的处理流程了:中断处理程序
1、切换堆栈和GDT
先来看看是如何切换的:
108 mov esp, StackTop ; 堆栈在 bss 段中
109
110 mov dword [disp_pos], 0
111
112 sgdt [gdt_ptr] ; cstart() 中将会用到 gdt_ptr
113 call cstart ; 在此函数中改变了gdt_ptr,让它指向新的GDT
114 lgdt [gdt_ptr] ; 使用新的GDT
115
116 lidt [idt_ptr]
117
118 jmp SELECTOR_KERNEL_CS:csinit
119 csinit: ; “这个跳转指令强制使用刚刚初始化的结构”——<<OS:D&I 2nd>> P90.
120
121 ;jmp 0x40:0
122 ;ud2
123
124 sti
125
126 hlt
sgdt和lidt分别是store和load GDT的意思,其中cstart改变了gdt_ptr,我们来看一下这个函数
22 PUBLIC void cstart()
23 {
24 disp_str("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n-----\"cstart\" begins-----\n");
25
26 // 将 LOADER 中的 GDT 复制到新的 GDT 中
27 memcpy( &gdt, // New GDT
28 (void*)(*((t_32*)(&gdt_ptr[2]))), // Base of Old GDT
29 *((t_16*)(&gdt_ptr[0])) + 1 // Limit of Old GDT
30 );
31 // gdt_ptr[6] 共 6 个字节:0~15:Limit 16~47:Base。用作 sgdt 以及 lgdt 的参数。
32 t_16* p_gdt_limit = (t_16*)(&gdt_ptr[0]);
33 t_32* p_gdt_base = (t_32*)(&gdt_ptr[2]);
34 *p_gdt_limit = GDT_SIZE * sizeof(DESCRIPTOR) - 1;
35 *p_gdt_base = (t_32)&gdt;
36 }
在csstart之中,我们用到了PUBLIC、PRIVATE等修饰符,也用到t_32等数据类型,要知道的是,这个程序现在已经是C语言的程序了。我们在这里已经用int等数据类型,想一想,此刻能不能用printf呢? 我们将打印函数进行封装处理,然后编译。需要注意的是,gcc在处理字符串的时候,会传入函数的是字符串地址,而且字符串的末尾会加上‘/0’,也就是ASCII编码0,这也是为什么dis_str能够找到末尾的原因。
2、整理我们的文件
到现在为之,我们的代码已经比较多而乱了,整理归类:启动和引导:booot.asm 和loader.asm放在目录boot中
库文件:klib.asm && string.asm放在/lib之中
内核:kernel.asm && start.c放在kernel之中
3、makefile
makefile的语法我们不打算在本文讲解,你可以参考这里:makefile快速入门
4、添加中断处理
操作系统中最重要的部分,可以算得上的进程调度。为了实现进程调度,我们需要一种控制权转换机制,这种机制变是中断。因为端口操作是写外部芯片的端口,所以可能会有一定的延迟,我们要在端口写命令后面加上延迟指令。对于全局变量的处理:我们需要全局变量在某个文件中是定义,而在其他文件中仅仅是申明,所以就有一个对extern进行处理的小trick。
如果你对这一部分有一些疑问,那么或许你应该回忆一下中断的处理流程了:中断处理程序
添加中断处理需要做哪些工作呢?
初始化8259A、初始化IDT、添加中断和异常处理函数。