编写实模式多任务操作系统模型之二.docx

上传人:b****2 文档编号:2266847 上传时间:2022-10-28 格式:DOCX 页数:17 大小:59.50KB
下载 相关 举报
编写实模式多任务操作系统模型之二.docx_第1页
第1页 / 共17页
编写实模式多任务操作系统模型之二.docx_第2页
第2页 / 共17页
编写实模式多任务操作系统模型之二.docx_第3页
第3页 / 共17页
编写实模式多任务操作系统模型之二.docx_第4页
第4页 / 共17页
编写实模式多任务操作系统模型之二.docx_第5页
第5页 / 共17页
点击查看更多>>
下载资源
资源描述

编写实模式多任务操作系统模型之二.docx

《编写实模式多任务操作系统模型之二.docx》由会员分享,可在线阅读,更多相关《编写实模式多任务操作系统模型之二.docx(17页珍藏版)》请在冰豆网上搜索。

编写实模式多任务操作系统模型之二.docx

编写实模式多任务操作系统模型之二

在前面,我们了解了X86模式的功能特点,运行机制并对程序模块进行了分析。

下面将继续分析程序模块的进程调度子程序和其他辅助程序,并完成这个程序的编译及安装运行。

进程调度子程序scheduler

scheduler是系统的进程调度程序,是实现多任务关键的部分。

它实质上是嵌入到系统时钟中断内来完成进程调度功能的。

由于系统时钟以18.2次/秒的频率发生中断,这样每当时钟中断一发生,便可带动进程调度程序执行,所以这个OS模型是以时钟中断来驱动的。

为了不影响原来时钟中断程序的例行工作,它开始运行时首先要调用位于ROMBIOS中的0xF000:

0xFEA5处的时钟中断服务程序。

返回后,它从current_task单元中找到当前被中断进程的进程号,根据该进程号在SPtable中找到其进程栈指针的存放地址,将其栈指针存入到该地址中,为下一次运行做好准备。

而后将current_task加1,得到下一个将被调度运行的进程号。

由该进程号在SPtable中找到上一次该进程被中断时的进程栈指针,将CPU的SP寄存器设为该值,这样堆栈指针便指向进程栈的栈顶。

由于每次进程被中断运行时在进程栈顶都保存有断点处的CPU内部通用寄存器及ES和DS的值,此时将这些值恢复到相应的寄存器中,最后通过一条IRET指令便可将CPU的控制权转移到这一进程上,这个进程便重新从断点处重新运行。

整个系统便在调度程序的控制下循环往复地运行,依次将CPU分时地切换到各个进程上。

由于切换的速度较快,所以宏观上我们便看到几个进程在同时向前并行推进,互不影响。

在微观上,几个进程轮流占用CPU,分时运行。

Scheduler初次被激活是Kernel全部初始化工作完成后。

Kernel初始化工作完成后,各个进程堆栈空间布局均如图7所示的形式,其中“Task1偏移地址”单元中为相应进程入口地址的偏移地址。

在Kernel程序中:

 

jmp$;kernel在此等待时钟中断的到来

该指令相当一条无穷循环指令while

(1);,当时钟中断到来时,它将从无穷循环中转到scheduler去执行,而后每一次时钟中断都引起scheduler的运行,这时的各个进程堆栈空间布局仍有如图7所示的形式,只不过此时堆栈的底端由于进程的运行已经有进程所压入的数据,留下了进程运行过的痕迹,如图8所示。

调度程序运行流程如图9所示。

 

具体工作步骤如下(假设调度程序运行前,CPU正处于task1的进程空间,SP指向task1的进程堆栈的栈顶,此时发生时钟中断,scheduler投入运行):

1.调度程序将task1进程断点处的CPU内部通用寄存器及ES和DS的值压入到task1进程堆栈中,代码如下:

 

pusha

pushes

pushds

 

2.向中断控制器8259发送EOI中断结束指令:

 

moval,0x20

out0x20,al

 

3.将标志寄存器Flag压入堆栈,调用位于ROMBIOS中的0xF000:

0xFEA5处的原时钟中断服务程序,使原时钟中断服务程序完成其原有的工作,代码如下:

 

pushf

call0xF000:

0xFEA5

 

由于原时钟中断为硬件中断,所以中断服务程序返回指令为iret。

iret指令依次将栈顶字单元的内容弹出到IP、CS及Flag中,这样为了保证上述call指令的正确返回及堆栈不被破坏,所以要在call指令的前面加入pushf指令,以和iret的动作相配合。

4.保存将被剥夺执行权的task1进程堆栈指针。

由于进程堆栈中保存有进程被中断时的断点信息,而其它进程运行时也要使用CPU内部的SP寄存器,所以在进行任务切换前要做进程的堆栈指针的保存工作。

如下指令所示:

 

movax,[current_task]

movbx,ax

shlbx,1

leasi,[SPtable]

mov[ds:

si+bx],SP

 

在current_task内存中存放当前运行进程的进程号,以为索引可以在SPTABLE数组中找到该进程堆栈指针的存放地址,即进程堆栈指针的存放地址=SPtable+2*进程号,找到该地址将进程断点处的SP存入其中。

5.切换到下一将被调度运行task2的进程堆栈空间。

由于在进程堆栈保存有进程的断点信息,所以找到进程堆栈的顶位置便可转入到该进程运行。

scheduler对于各进程的调度采用轮转式的方式,即按照task1-->task2-->task3的顺序,而后再从task1开始依次循环,使各个进程得以运行。

所以将current_task的值增1,再判断current_task是否超过最大进程数MAXTASKS,若超过,则使其复位,从0开始重新计数即可。

取得合适的current_task值后,按上面步骤的公式便可找到将被调度进程的堆栈指针的存放地址,从中取出值送到SP寄存器即可。

如下段程序代码所示:

 

incax;取得下一将被调度进行的进程号

cmpax,MAXTASKS;该进程号是否超过最大进程数

jbmove_on;若没超过,跳到move_on继续

xorax,ax;超过最大进程数,清0,从第一个进程开始调度

move_on:

mov[current_task],ax

;将更改后进程号送入current_task单元保存,按公式进程堆栈指针的存放地址=SPtable+2*进程号

取得进程堆栈指针

movbx,ax

shlbx,1

leasi,[SPtable]

movSP,[ds:

si+bx];将进程堆栈指针送到SP,切换到进程堆栈空间

6.恢复断点信息,使下一被调度进程从断点处运行。

 

popds

popes

popa;恢复所有被保存的CPU内寄存器值

iret;激活被调度进程

 

其它辅助程序

系统中存在3个被调度的程序,分别为task1、task2、task3。

由于这是一个实验程序,所以它们的功能较为单一,主要是将自身的一个16位计数器进行循环计数,而后通过直接写屏方式在显示器上显示出来。

由于现行机器的运行速度较快,为使用户能看清计数器的变化过程,在各个进程的执行过程中加入了人为的延时控制。

每一个进程都是一个无限的循环过程,只要调度程序在运行,各个进程就会无限运行。

此外系统中还有几个辅助程序:

◆16进制数显示子程序printhex

这个子程序主要是为系统内的3个演示进程服务,它可以将从堆栈传递过来的一个16位的数以16进制形式显示到屏幕的指定位置,通过直接写屏方式在显示器上显示出来。

在VGA的80X25彩色文本显示模式下,一个字符在显示缓冲区上占2个字节的空间,其中所显示字符的ASCⅡ码存于低地址单元中,字符的显示属性(前景、背景、高亮和闪烁等)存于高地址单元中,如果将字符及其显示属性存储位置弄错,将会产生花屏的现象。

Printhex根据显示位置参数计算出所需显示的准确位置,避免产生花屏的现象。

调用者只需提供合法的显示位置即可,行号应在1~25之间,列号在1~80之间。

◆键盘中断子程序keybd

它是一个基本的内核级键盘中断处理程序,显示键盘的ASCⅡ码到屏幕上。

它不受调度进程的控制,只要有键盘中断发生,该中断服务程序就会运行。

在该程序被触发运行时,从60H端口读入键盘的扫描码。

扫描码最高位的状态标志着其是键入码还是释放码,如为释放码则不进行处理,直接返回;若为按键的压入码,则以键入码作为索引在键盘扫描码/ASCⅡ码对照表查得对应的ASCⅡ码,通过int10h功能调用将其输出到显示器上。

在键盘中断返回前,要向键盘内的单片机发复位信号,先将61h端口的最高位置1,而后再置0,这样键盘才不会死锁。

kernel.asm完整代码及注释如下:

 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;内核程序kernel.asm

;在系统时钟中断的驱动下,依次使系统内的3个进程轮流占用CPU完成各

;自的工作:

在屏幕上指定位置循环显示自己的计数器。

具有内核级键盘中

;断,显示出键盘ASCII码

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

[BITS16]

[ORG0x00]

STACKSIZEequ0x400;各进程的堆栈空间大小为1k个字节

MAXTASKSequ3;最大进程数

VIDEORAMequ0xb800;显示缓存段地址

KERNELSEGequ0x8000;kernel程序段地址

;------------------------------------------------------------------

start:

movax,KERNELSEG

movds,ax;数据段,堆栈段寄存器的

movss,ax;值均设为KERNELSEG

movsp,0xFFFF;kernel堆栈指针=0xFFFF

movdx,0x3f2

moval,0x0c

outdx,al;关闭软驱马达

movax,0x0003

int0x10;清屏

movdx,0x0600;进程的堆栈基地址

adddx,STACKSIZE;进程1的堆栈指针=0xA00

movax,task1;进程1入口地址

calltaskinit;初始化进程1

adddx,STACKSIZE;进程2的堆栈指针=0xD00

movax,task2;进程2入口地址

calltaskinit;初始化进程2

adddx,STACKSIZE;进程3的堆栈指针=0x1000

movax,task3;进程3入口地址

calltaskinit;初始化进程3

;重新设置系统定时器的中断向量

cli;关中断,以免在设置中断向量发生意外

movax,KERNELSEG

pushds;保存ds值

movbx,0

movds,bx;ds=0

movword[ds:

0x08*4],scheduler;定时器中断向量偏移送到0:

20~0:

21

movword[ds:

0x08*4+2],ax;定时器中断向量段地址送到0:

22~0:

23

;重新设置键盘的中断向量

movword[ds:

0x09*4],keybd;键盘的中断向量偏移送到0:

24~0:

25

movword[ds:

0x09*4+2],ax;键盘的中断向量段地址0:

26~0:

27

popds;恢复ds值

sti;开中断,允许中断进入

jmp$;kernel在此等待时钟中断的到来

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;被调度进程task1

;功能:

对于自身的计数器进行计数,而后在屏幕的3行1显示输出

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

task1:

.l1decword[task1ctra];产生短时间延迟,以免计数显示过快

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 人文社科 > 法律资料

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1