标签:实例 破坏 intel 代码 style 寄存器 不同的 产生 知识点
为什么会出现进程这个概念
进程切换会产生什么影响
哪些状态需要被保护呢
如何保存这些状态呢
如何调度进程
时钟中断
中断重入
如果没有进程这个概念,程序是如何执行的呢?那只能运行一个程序,运行完一个程序,才能接着运行下一个。哪怕上个程序中间要等待某件事十天半个月,也是没有办法的事情了。
如果有进程这个概念呢?那么操作系统看到有个进程闲了,就不把cpu分给它了,这就是进程切换的概念。这样cpu利用率就上来了。此时还要考虑一个情况,要是一个进程一直忙,难道其他进程就只能在旁边等着吗?又规定每个进程最多能连续使用多久的cpu时间,到时间必须让给别人,这就是所谓的进程时间片。
而由于进程的出现,可能会引发这样一个问题,某一个进程崩溃了,大家都一起玩完了。为了避免这样的情况出现,intel引入了保护模式的概念。一个进程挂了,给操作系统一个机会,清除这个进程,而不是和它一起灭亡。
在保护模式,intel规定了不同的特权级。linux系统用0和3两个特权级。关于特权级,见保护模式下处理器特性那篇文章。咱们这个实践过程中用了三个特权级,0、1、3,比linux多用了一个1特权级,这是因为作者用的微内核,对于一些系统的功能模块给了1这个特权级,比内核低,比用户进程高。
不同的进程执行的是不同的代码,同一个进程不同的特权级这两个问题又带来了堆栈切换的问题。因为不同的特权级、不同的进程,如果共用一个堆栈,谁也不能保证再切回去的时候堆栈没有被破坏。所以我们必须保护好切换前的执行环境。
哪些状态需要被保护呢。
上帝的归上帝,撒旦的归撒旦。进程的当然归进程自己了。
又需要一个账本了:
这些就完了吗?肯定不可能一次考虑周全,只能随着工作的推进,我们的考虑才能越来越完善。
那么这些东西保存在哪里呢?如果随便保存在一个地方,系统还需要额外做个记录,既然每个进程都独有一份不同的记录,并且所需的空间是固定的,不会一会儿大一会小。这样的话,最好就是放在进程表里,既免去了系统要做的额外记录,而且进程本身如果要对这些值做修改是非常方便的(例如exec加载一个新的镜像)。
保存的地方有了,似乎就应该可以切换了。来看看有几种切换方式。
前两种状态比较好处理,都是按照保存在进程表中的寄存器顺序依次弹出即可。
单单是从用户态到内核态有点麻烦,因为用户态的时候你不能访问内核态的数据,那么你怎么会知道你的进程表在哪里呢?那么用户态的执行环境就没法保存了。
intel从硬件上给予了支持。在从用户态切换到内核态的时候需要用到tss段了。当因为某些信号从用户态切换到内核态时候,cpu会把ss、esp暂时保存在cpu中,然后从tr寄存器中拿到选择子,从GDT中得到tss的段基地址,然后将tss.ss0给ss寄存器,tss.esp0给esp寄存器,这两个值就相当于从用户态切换到内核态时候的堆栈指针。再把刚才保存的ss、esp加上EFLAG、cs、eip放到esp0指向的栈上。
这就要求进程在离开内核态回到用户态的时候,必须要把tss段中的ss0和esp0的值设置为自己进程表中保存用户执行环境的起始地址。
保护进程的现场,好像又引出了不少东西。GDT、LDT、TSS。其实它们几个都是内存中的一张表。网上都有说明。
说一下以前自己容易忽略的LDT寻址的细节。
当TI=1时表示段描述符在LDT中:
似乎进程这个值得费笔墨的知识点真的没有什么东西可以说的了。
也难怪,伟大的东西似乎都是简单的。
那进程切换的时机呢?总不能让进程自己决定什么时候不用cpu了吧?万一有个无赖怎么办?
这个时候,中断就是一个非常好的仲裁者了,依靠物理特性,准时准点的报时。系统只需要在石英晶体报时的时候做一些计算就可以了。
在时钟中断里面,我们可以来判断当前进程是不是把时间片用完了,那系统从准备好的进程队列中挑选一个进程来执行。当然,这个挑选的过程可以简单也可以复杂,看实现者想做什么了。目前我们要做的比较简单,每个进程分配一些时间片,每次时钟中断里面把当前进程的时间片减1,等于0的时候,被系统拿掉cpu的使用权,让下一个进程使用cpu。
时钟中断还是比较简单的,设置好8259A就可以响应中断信号了。但是在书中,作者又让我了解到中断重入的概念。以前看linux 0.12的时候时钟中断的代码中却是没有重入的概念。
来看一下cpu响应中断时候会发生什么:
CPU响应中断后,输出中断响应信号,自动将状态标志寄存器的内容压入堆栈保护起来,然后将状态标志寄存器中的中断标志位IF与陷阱标志位TF清零,从而自动关闭外部硬件中断。因为CPU刚进入中断时要保护现场,主要涉及堆栈操作,此时不能再响应中断,否则将造成系统混乱。
书中给出的实例是保存了寄存器现场后用sti指令打开了中断,所以允许中断重入,而linux 0.12中进入中断后没有做额外的处理,所以不用理会中断的重入。
处理中断重入也不算难,需要定义一个全局变量,并初始化为-1。进入中断对这个变量加1,如果本次的计算结果不等于0,说明是重入的中断,直接结束本次中断。
标签:实例 破坏 intel 代码 style 寄存器 不同的 产生 知识点
原文地址:http://www.cnblogs.com/shangye/p/6186461.html