30天自制操作系统学习笔记----day_six

一、实验主要内容

1.分割源文件

重点内容:

  1. 分割源文件的优点:按照处理内容进行分类,在修改文件时容易找到地方、如果Makefile写得好,只需编译修改过的文件就可以提高make的速度、分成单个源文件比较小,缺点:源文件数量增加、分类得不好,不易于定位文件。
  2. 结构图:

       

  1. 流程图:

(4)make run之后文件目录会多出graphic.c和dsctbl.c以及一些编译中间过程的文件,得到的最终结果还是和未分割前一样,只不过是将源文件分割成多个文件去并行编译,提高了make的速度。

2.整理Makefile

关键代码:

 

重点内容:

  1. (CC1)和(GAS2NASK)起到索引作用,通俗的讲就是告诉这段代码用CC1程序去生成相关文件。该段代码的意思是Makefile根据bookpack.c文件生成.gas文件,然后通过CC1或者GAS2NASK生成中间文件或者进行链接。
  1. 整理头文件

重点内容:

  1. 由于在bookpath.c里面声明了大量函数的名称,为了缩短代码量,将这些名称整理到.h文件里面去,.h文件通常称为头文件或者库,里面声明了各种函数,具体的实现在.c文件。如何使用呢?在使用的程序下用include “xxxxx.h”即可导入使用。
  2. 头文件放在程序头部的原因:声明必须一开始就让编译器知道,同俗地讲就是让编译器实现知道自己编译的东西有哪些,而不是遇到未定义的再去查询,这样会大大降低编译的速度。
  3. 在include库文件的时候,” ”和< >的区别,前者是因为.h文件和.c文件在同一个文件目录,后者是在封装好的库文件目录。库文件命名一般是lib,要是懒得加“”,那么就将你的.h文件添加到编译器的库文件目录去。
  1. 意犹未尽

关键代码:

代码的含义:

如何理解该段代码?[ESP+4]存放着段上限,[ESP+8]存放着地址(GDT开始的地址),作者为了达到[FF FF 00 00 00 27 00]的目的,那么就需要将0x0000ffff读取到[ESP+6]的位置才能和[ESP+8]的地址组成作者想要的。每一个内存单元是2个字节,从6开始读取刚好能读够6个字节。

重点内容:

  1. 该函数的作用是将指定的段上限和地址赋值给GDTR的48位寄存器,因为GDTR的特殊性,不能通过mov指令来完成赋值。赋值方法如下:在定一个内存地址,然后读取6个字节也就是48位,最后赋给GDTR寄存器即可。该过程被封装成LGTD指令(Loading GTDR)。
  2. GDTR寄存器结构图:

低16位为段上限,大小等于GDT的有效字节数-1,剩余的高32位是GDT的开始地址。

  1. 额外扩展1:

段选择子(Selector)由GDTR访问全局描述符表是通过“段选择子”(实模式下的段寄存器)来完成的。段选择子是一个16位的寄存器(同实模式下的段寄存器相同)。

如图:

段选择子包括三部分:描述符索引(index)、TI、请求特权级(RPL)。它的index(描述符索引)部分表示所需要的段的描述符在描述符表的位置,由这个位置再根据在GDTR中存储的描述符表基址就可以找到相应的描述符。然后用描述符表中的段基址加上逻辑地址(SEL:OFFSET)的OFFSET就可以转换成线性地址,段选择子中的TI值只有一位0或1,0代表选择子是在GDT选择,1代表选择子是在LDT选择。请求特权级(RPL)则代表选择子的特权级,共有4个特权级(0级、1级、2级、3级)。

关于特权级的说明:任务中的每一个段都有一个特定的级别。每当一个程序试图访问某一个段时,就将该程序所拥有的特权级与要访问的特权级进行比较,以决定能否访问该段。系统约定,CPU只能访问同一特权级或级别较低特权级的段。

  1. 额外扩展2:

局部描述符表LDT(Local Descriptor Table)局部描述符表可以有若干张,每个任务可以有一张。我们可以这样理解GDT和LDT:GDT为一级描述符表,LDT为二级描述符表。如图

LDT和GDT从本质上说是相同的,只是LDT嵌套在GDT之中。LDTR记录局部描述符表的起始位置,与GDTR不同,LDTR的内容是一个段选择子。由于LDT本身同样是一段内存,也是一个段,所以它也有个描述符描述它,这个描述符就存储在GDT中,对应这个表述符也会有一个选择子,LDTR装载的就是这样一个选择子。LDTR可以在程序中随时改变,通过使用LLDT指令。如上图,如果装载的是Selector 2则LDTR指向的是表LDT2。举个例子:如果我们想在表LDT2中选择第三个描述符所描述的段的地址12345678h。

a. 首先需要装载LDTR使它指向LDT2 使用指令lldt将Select2装载到LDTR。b. 通过逻辑地址(SEL:OFFSET)访问时SEL的index=3代表选择第三个描述符;TI=1代表选择子是在LDT选择,此时LDTR指向的是LDT2,所以是在LDT2中选择,此时的SEL值为1Ch(二进制为11 1 00b)。OFFSET=12345678h。逻辑地址为1C:12345678h

c. 由SEL选择出描述符,由描述符中的基址(Base)加上OFFSET可得到线性地址,例如基址是11111111h,则线性地址=11111111h+12345678h=23456789h

d. 此时若再想访问LDT1中的第三个描述符,只要使用lldt指令将选择子Selector 1装入再执行b、c两步就可以了(因为此时LDTR又指向了LDT1)

由于每个进程都有自己的一套程序段、数据段、堆栈段,有了局部描述符表则可以将每个进程的程序段、数据段、堆栈段封装在一起,只要改变LDTR就可以实现对不同进程的段进行访问。

当进行任务切换时,处理器会把新任务LDT的段选择符和段描述符自动地加载进LDTR中。在机器加电或处理器复位后,段选择符和基地址被默认地设置为0,而段长度被设置成0xFFFF。

  1. 额外扩展3:

访问GDT

段描述符在GDT中

当TI=0时表示段描述符在GDT中,如上图所示:

①先从GDTR寄存器中获得GDT基址。

②然后再GDT中以段选择器高13位位置索引值得到段描述符。

③段描述符符包含段的基址、限长、优先级等各种属性,这就得到了段的起始地址(基址),再以基址加上偏移地址yyyyyyyy才得到最后的线性地址。

(6)额外扩展4:

访问LDT

当TI=1时表示段描述符在LDT中,如上图所示:

①还是先从GDTR寄存器中获得GDT基址。

②从LDTR寄存器中获取LDT所在段的位置索引(LDTR高13位)。

③以这个位置索引在GDT中得到LDT段描述符从而得到LDT段基址。

④用段选择器高13位位置索引值从LDT段中得到段描述符。

⑤段描述符符包含段的基址、限长、优先级等各种属性,这就得到了段的起始地址(基址),再以基址加上偏移地址yyyyyyyy才得到最后的线性地址。

(7)关键代码:

如何理解该段代码呢?该结构体共8个字节的大小,包含了段的大小、段的起始地址、段的管理属性。该段代码使用位移和与运算将将各个字节填入相应的字节,也就是对IDTR进行设置。由于段的最大上限是4GB,共32位,如果放到结构体里面需要占掉4个字节,那么就没有位置来保存管理段的属性了,所以段上限只能用20位。这个时候就只能用1MB的内存了,显然不是这样,段里面有一个标志Git,当Git为1时,此时limit的单位被解释为页,一个页的大小是1kb,可以使用的空间为1M x 4KB = 4GB。

  1. 初始化PIC

重点内容:

  1. PIC是可编程中断控制器的缩写,是一种将8个中断信号集合成1一个中断信号的装置。
  2. PIC监控着8个引脚的输入,只要有一个信号输入,唯一的输出管脚的信号变为ON。
  3. PIC与CPU的连接结构:与CPU直连的称为主PIC,负责处理0-7号中断信号。与主PIC相连的为从PIC,负责处理8-15号中断信号。(从PIC通过IRQ2与主PIC相连接)
  4. 关键代码:

基本上都是对某些控制寄存器的某一位进行置1,从而开启某种模式。

PIC寄存器介绍:均是8位寄存器

a)、IMR:中断屏蔽寄存器 分别对应IRQ0 - IRQ7,被置为1的引脚则被屏蔽,PIC就会忽略该引脚传入的信号。这样做的原因是防止如静电干扰、或者接受多个信号从而引起操作系统混乱。

b)、ICW:初始化控制数据,有4个,分别编号为1-4,共有4个字节的数据。

c)、ICW1ICW4与PIC主板配线方式、中断信号的电气特性有关。

d)、ICW3有关主从连接的设定。

e)、ICW2决定IRQ以哪一号中断通知CPU。

6.中断处理程序的制作

关键代码:

重点内容:

  1. 鼠标为IRQ12,键盘是IRQ1。
  2. 由于执行完中断后不能return,需要借助IRETD,故naskfunc.nas修改为下图内容:

汇编可以从调用中断函数处返回继续执行。

  1. 缓冲器就相当于一个队列,先进来的数据总是先出去,称为FIFO,栈则相当于一个柱形有底的容器,先进来的数据总是最后一个被取出,称为FILO。
  2. 栈操作数据的指令:进栈:PUSH; 出栈:POP ,栈指针为ESP,时刻指向栈顶位置,栈的一个位置大小为4个字节。因此可以通过ESP+4,ESP+8等等来访问栈的数据。
  3. 一个不常见的指令:PUSHAD,相当于:

POPAD也是如此。反过来而已。

  1. CALL在汇编语言中是调用函数的指令,调用前会保存下一条指令的地址,调用结束后返回改地址继续执行,称为函数返回地址。
  2. 注册函数到IDT中:在中断处理函数dsctbl.c的init_gdtitd()加以下代码即可。

  1. 最后修改PIC的IMR来接受鼠标中断即可完成中断程序的制作。
  2. 运行截图:

按下键盘A:显示中断信息:

再次运行:单击画面将鼠标与QEMU绑定在一起,左右移动鼠标发现没有动!作者在骗人的!

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值