《自己动手写操作系统》总结(下)

来源:互联网 发布:淘宝新店铺推广怎么做 编辑:程序博客网 时间:2024/04/30 06:34

上次说到了 将控制权交给内核,接下来的工作都由内核来完成。从这里开始,就可以用C语言来写了。(为什么?我还是不明白)但并不是内核全用C写,它依旧需要一个用汇编写得开头,让loader跳入进来。

跳进来后需要立即做一件事:切换堆栈。因为原来的堆栈在内存中的位置是在loader中,而将来loader是会被我们覆盖的,所以必须切换。设好堆栈也是调用C函数的前提条件。这里说一下,所谓的堆栈只是一段连续的内存,你分配好内存后,只要将这段内存的最高地址赋给ESP(还有段寄存器SS也要相应处理),那这段内存就是当前所用的堆栈了。

接着要切换GDT,就是把就的GDT复制到自己希望的内存地址中,可以用C语言完成。注意! 在使用 C 代码的时候一定要保证 ds, es, ss 这几个段寄存器的值是一样的,因为编译器默认它们是一样的。(这里又想到了平坦内存模型的问题。这种模型有什么优势或者好处?对于系统保护有什么意义?问题还有好多)把GDT放在什么位置也是需要考虑的,因为这张GDT在今后一直需要用到,不能被其他内容所覆盖,所以需要考虑内存的布局。

接着是初始化中断处理,包括设置8259A和IDT。IDT有256项,但并不需要全部都初始化。接着就是中断处理函数(需要用汇编写,中间可以调用C函数)。好了之后加载IDT。这都挺简单的。这些好了之后,如果因为代码编写出现问题而导致异常,系统就不会自动崩溃,而是调用相应中断处理函数(我碰到的最多的就是#GP,主要是跳转地址错误……尤其是中断返回的时候)。

接下来讲的是进程。感觉书中只是对进程在内核中的表现形式做了介绍。每个进程需要一个LDT来支持,这很简单,和GDT一样,就不多说了。重点主要是进程的中断和切换的原理。进程要能在被中断后恢复,就需要把在进程中断前一刻的所有寄存器值全保存下来,在恢复时只要把全部寄存器值还原,进程就可以像什么都没发生过一样继续运行。

如何保存?我们知道在中断发生时,会把返回地址和标志寄存器压栈,同时由于特权级转换,还会把原ss和esp压栈,然后切换堆栈(TSS)。所以需要模拟一个堆栈的数据结构(进程表),来存放所有寄存器的值(cs和eip是不能用push压栈的),然后在中断产生前,设置好TSS中的esp0值,指向该结构的顶端。中断程序中则push相应寄存器(由于目前esp是取得TSS中esp0的值,所以寄存器被压进了进程表中保存起来),然后切换到内核栈。

而切换进程则是在中断结束前,重新设置好esp0(指向目标进程表的顶端,为下次中断准备)和当前esp值(指向目标进程的进程表底端),然后恢复相应寄存器,中断返回即可,这样所有寄存器都被还原,esp也被切换到了目标进程运行时的堆栈。

这挺复杂的,需要多多体会。

有个问题是,上面的实现都必须要有特权级切换,才能切换堆栈,完成我们的任务。这是不是说不发生特权级切换的话就不能进  行进程切换的工作?还是有什么别的方法?

编写中断程序也是一件很艺术的事,说不清,需要多看代码,才能体会。其实对中断,我还有许多的不了解,比如中断重入。控制是否接收中断的条件有,1:eflags中的中断屏蔽标志位,用sti和cli控制,2:8259A中的EOI,3:8259A中的屏蔽寄存器。3不是在中断程序中控制的,不与讨论。中断发生后,我们需要重置EOI,才能使8259A继续接受后面的中断。8259A中还有一个寄存器,保存还未处理的中断。我还没看过详细的资料,所以接下来的纯属猜测。当有中断产生,8259A先看自己的中断屏蔽寄存器,如果中断没有被屏蔽,则放入中断服务寄存器。8259A会检查EOI位,如果是1则表示CPU正在处理中断,不做任何事情,如果为0且中断服务寄存器中有中断,则发送中断请求。而CPU会按照eflags的标志予以处理和抛弃。

进入中断后硬件会自动关闭中断标志,所以如果允许中断重入,需要sti。我们还需要重置EOI。这时8259A则同时会重置中断服务寄存器中相应的位,表示中断结束(EOI只有一位,而中断服务寄存器中有很多位,如何对应?猜测是自动复位最高优先级位)。

中断就此说完,接下去的内容我也没好好看过,所以暂时就不说了。下一篇文章介绍硬盘控制器编程^_^

至此,结束。

PS:有什么问题请留言,我会经常来看的 谢谢

原创粉丝点击