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

上传人:b****6 文档编号:11926748 上传时间:2023-06-03 格式:DOCX 页数:16 大小:150.03KB
下载 相关 举报
编写实模式多任务操作系统模型之一.docx_第1页
第1页 / 共16页
编写实模式多任务操作系统模型之一.docx_第2页
第2页 / 共16页
编写实模式多任务操作系统模型之一.docx_第3页
第3页 / 共16页
编写实模式多任务操作系统模型之一.docx_第4页
第4页 / 共16页
编写实模式多任务操作系统模型之一.docx_第5页
第5页 / 共16页
编写实模式多任务操作系统模型之一.docx_第6页
第6页 / 共16页
编写实模式多任务操作系统模型之一.docx_第7页
第7页 / 共16页
编写实模式多任务操作系统模型之一.docx_第8页
第8页 / 共16页
编写实模式多任务操作系统模型之一.docx_第9页
第9页 / 共16页
编写实模式多任务操作系统模型之一.docx_第10页
第10页 / 共16页
编写实模式多任务操作系统模型之一.docx_第11页
第11页 / 共16页
编写实模式多任务操作系统模型之一.docx_第12页
第12页 / 共16页
编写实模式多任务操作系统模型之一.docx_第13页
第13页 / 共16页
编写实模式多任务操作系统模型之一.docx_第14页
第14页 / 共16页
编写实模式多任务操作系统模型之一.docx_第15页
第15页 / 共16页
编写实模式多任务操作系统模型之一.docx_第16页
第16页 / 共16页
亲,该文档总共16页,全部预览完了,如果喜欢就下载吧!
下载资源
资源描述

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

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

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

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

操作系统是计算机软件系统的重要组成部分,它起着管理计算机软硬件资源、控制计算机的正常运行及为各类用户提供使用接口等诸多功能,是其它各类应用软件赖以存在的基础。

由于操作系统软件多数以公司模式集体开发,并以成品软件的形式进行发售,使得用户只能局限于操作系统所提供的使用界面及系统功能调用上,对于系统底层的运行机制的理解只能局限于一些表面现象上,不能深入把握操作系统内部的运作机理,即使以源码形式提供的,也由于其规模庞大、特征繁多,使得用户不可能在短时间内完全掌握运行机制。

为了能充分了解操作系统的功能实现,自己亲自动手编写一个小型的,或是实现部分功能的操作系统模型,而后逐步完善不失为一种好方法。

本文所述的这一操作系统模型即是在这方面的一次尝试。

功能及特点

1.实现在x86实模式下分时的多任务功能。

2.PC机自检完成后可以完成机器的引导。

3.引导成功后可以使3个进程并行运行,分别运行于自身的程序空间内,完成自身计数器的计值工作,并采用直接写屏方式输出到屏幕上。

4.有一个内核级键盘中断处理程序。

5.软件规模小巧,易于研读、理解和实践,以及进行功能的增强和拓展。

6.程序分模块编写,功能明确,界限清晰。

7.对于机器的配置要求极低,以Intel80x86处理器为CPU的PC机、VGA彩色显示器、3英寸软驱即可。

运行机制

众所周知,Intel80386以上的CPU具有实模式和保护模式两种工作方式,处于保护模式的CPU在硬件上支持存储器管理、虚拟地址、分页、保护等功能,具有多任务切换机制,是现今的多用户多任务操作系统如Windows、Linux、FreeBSD等所采用的工作方式。

而工作于实模式80386及以上的CPU相当于高性能的8086CPU,本身在硬件上不具备多任务的切换机制,那末,在x86的实模式下是否可以实现现多任务功能呢?

答案是肯定的,利用软件进行模拟。

本程序在系统时钟中断的驱动下,利用软件方式在x86CPU实模式下实现了一个多任务操作系统模型,可以启动机器,并在屏幕上显示由3个并行进程输出的各自程序计数器。

在宏观上,3个进程在并行运行。

在微观实现上,通过时钟中断的控制,3个进程依次轮流占用CPU完成各自的工作,即每当发生时钟中断,Kernel内的调度程序被激活,顺序将下一个先前被剥夺运行权的进程赋予运行权,使其可以占用CPU运行,当再次发生时钟中断时,当前运行的进程被强行中断运行,调度程序将其断点信息保存到该进程的堆栈区,待下次运行时再将其恢复,而后调度程序选择下一进程,使其转入运行状态。

整个系统循环往复,直到关机。

相关背景知识

本程序全部利用汇编语言在x86实模式下编程,为能深入理解程序的工作原理,必须了解PC机相关软硬件知识。

下面对相关内容进行简单叙述。

1.PC机自举过程简述

PC机在通电后至操作系统接管控制前,要进行一系列的自检及初始化过程,包括对内存、外设等硬件的检测。

在检测过程中若存在严重错误则停机。

若无则试图根据CMOS内的配置信息读取启动盘上的引导记录bootsector。

通常先从软盘启动(本文默认从软盘启动),将软盘上0面0道1扇区的512字节读入到内存0000:

7c00h地址处,而后跳到该地址处执行,这时机器便由软件接管控制。

通常操作系统都由一个引导程序进行引导,引导程序的主要作用在于将操作系统的内核读入内存,而后转到内核去执行,由内核接管机器的所有控制,包括内存管理、进程管理、设备管理、处理机管理等。

上述启动盘上的引导记录就是一个引导程序,在该操作系统模型的实现中,boot.asm程序即起到这样功能的程序,后面将结合具体代码进行详细介绍。

2.PC机的中断向量表结构

中断是改变程序运行环境的一种机制,它可以使CPU暂停当前程序的执行,转去执行引发中断的中断源的中断服务程序。

在中断服务程序执行完毕后,再接着原来的程序执行。

中断可以通过中断指令intxx或由外部的硬件设备产生。

中断向量表即提供当中断发生时,如何转向中断服务程序的方法。

在中断向量表中存放了相应中断服务程序的入口地址,这一入口地址称为中断向量,它由段地址和偏移组成,即CS:

IP。

PC机中的中断向量表位于低端内存区的1KB存储器中,绝对地址为:

00000H~003FFH,其中的每个中断向量占4个字节,CS占2个字节(占高地址区),IP占2个字节(占低地址区)。

给定一个中断类型号,即可通过如下的方式取得中断向量的段地址及偏移:

CS=[中断类型号*4]

IP=[中断类型号*4+2]

3.PC机屏蔽中断的内部处理过程

PC机中断发生时,若中断标志I=1(中断允许),则有如下的内部动作:

◆a.将中断类型号*4,得到中断向量表的指针;

◆b.将CPU的标志寄存器Flag入栈;

◆c.将中断标志I和陷阱标志T清0;

◆d.将主程序断点处的CS及IP分别压入堆栈保存;

◆e.从中断向量表中取得中断服务程序的入口地址,分别送到CS和IP中;

◆f.转到中断服务程序去执行。

对于软件中断,则不判断中断标志的状态,直接执行a~f的处理过程。

在用户所写的中断服务程序的末尾,要有一条中断返回指令iret,控制程序流由中断服务程序返回到主程序中,它所完成的内部处理如下:

◆将先前压栈保存的端点从堆栈弹出,送到CS和IP中;

◆执行popf操作,恢复标志寄存器Flag;

◆从主程序的端点处继续运行。

4.PC机的过程调用

PC机的过程调用通过call指令产生,它的处理过程与中断过程相似,但无标志寄存器的处理,具体如下所示。

远过程调用:

callfarfarentry

◆将返回地址(call指令的下一指令)的CS及IP压栈;

◆以farentry的偏移及段地址送入IP及CS;

◆转到farentry去执行。

远过程调用返回:

RETF

◆将返回地址弹出到IP及CS;

◆从返回地址处继续执行程序。

对于近过程调用,则调用时只保存返回地址的IP,过程结束时只恢复IP。

5.汇编语言相关知识

本程序在Linux下用nasm进行编译。

nasm是一个功能强大的开源代码的汇编语言编译程序,其语法与Intel语法相似,可以产生纯二进制的指令代码,也可以产生a.out及ELF格式的目标代码,通过连接器连接便可生成直接由操作系统运行的程序。

这里用它产生二进制格式的指令代码,这是一种内存映像,加载到内存便可直接运行。

关于NASM编译器的详细信息可参考相关资料。

程序模块分析

本操作系统模型由两个程序组成,一个为引导程序boot.asm,另一个为内核程序kernel.asm。

其中引导程序位于软盘的引导扇区,主要功能为将Kernel从盘上读入内存,让其运行;Kernel则依次使3个进程占用CPU,完成各自的工作。

引导程序boot.asm

引导程序boot.asm位于引导软盘的0面0道1扇区,共计512个字节长度。

该段程序在系统通电自检完成后,将由ROMBIOS读到内存的0000:

7c00h地址处,并从该地址开始执行。

该程序运行后将首先将自身移动到高端内存区9000:

7c00h,留出低端内存区以备功能扩展时使用,而后转到高端内存区执行。

接着对引导驱动器复位,利用int13h的物理读盘功能,将位于0头0道2扇区及3扇区的kernel程序读入内存8000:

0000h地址处。

若读盘失败,则进行3次尝试;若仍未读出,则显示“kernelLoadingfailed!

Anykeytoreboot...”信息,而后重新启动;若读盘成功,则跳到kernel程序的入口点8000:

0000h地址处执行。

程序流程如图1所示。

boot.asm执行后的内存布局如图2所示。

由于在实模式下编程,程序可访问的内存空间为1MB,绝对地址从00000H~FFFFFH,其中低端内存及高端内存的部分区域已经被系统占用,程序可用的内存区为位于高端及低端的中间部分。

由于本程序规模较小,内存空间足够用。

对于本程序,高端97C00H~97DFFH为引导程序自搬移后的位置,80000H~803FFH为成功读入的内核代码所占的内存空间。

boot.asm的程序代码及详细注释如下:

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

;;boot.asm-OS引导扇区程序Bootloader

;;功能:

将自身移动至内存高端,而后将启动盘上的kernel读到内存,

;;接着跳转到kernel....

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

[BITS16];实模式16bit代码结构

;标号等价伪定义

INITSEGEQU0x07c0;启动扇区段地址

NEWSEGEQU0x9000;引导程序移动到的高端段地址

KERNELSEGEQU0x8000;kernel段地址

KERNELOFFSETEQU0x0000;kernel偏移地址

REBOOTSEGEQU0XFFFF;重启动段地址

REBOOTOFFSETEQU0X0000;重启动偏移地址

org0x7c00;汇编起始地址

start:

jmpshortbootup;跳转到启动代码

;数据区

;变量定义

bootdrvdb0;bootdrv用于保存启动驱动器号

retriesdb3;重复读盘次数

;系统启动信息

bootmsgdb"Systembootup...",0dh,0ah,00h

;kernelloading信息

loadknlmsgdb"Loadingkernel...",0dh,0ah,00h

;kernelloading错误信息

loadknlerrmsgdb"Kernelloadingfailed!

",0dh,0ah,"Anykeyto

reboot...",0dh,0ah,00h

bootup:

mov[bootdrv],dl;保存启动驱动器号

;显示启动信息

movsi,bootmsg

calldispstr

;将自身的所有程序及数据由0000:

7c00h移动到

;内存高端9000:

7c00h,总计512字节长度

cld;方向标志置0,

movax,NEWSEG;ax=0x9000

moves,ax;附加段寄存器es设为0x9000,所移数据的目的段

movdi,start;目的变址寄存器=7c00h

movsi,start;源变址寄存器=7c00h

movcx,0x200;移动总字节长度512Bytes

cli;禁止中断

repmovsb;执行移动操作

;跳转到移动后的新段,从there地址开始执行.

JMPNEWSEG:

there

there:

;这里是新的执行起始点.

sti;开中断

;下一步,将kernel程序从启动驱动器的读到内存中,而后由kernel接管控制.

movsi,loadknlmsg

calldispstr;显示kernel加载信息"kernelloading..."

readagain:

movah,0

movdl,[bootdrv]

int0x13;复位启动驱动器

;将位于从物理2扇区开始的2个扇区的kernel程序从启动驱动器的读到内存中

;KERNELSEG:

0000h

cld;方向标志置0

movbx,KERNELOFFSET;kernel地址偏移

movax,KERNELSEG;kernel段地址0x8000

moves,ax

movax,0x0202;AH=02(读扇区功能号),AL=2(扇区数)

movcx,0x0002;CH-磁道号,CL-逻辑扇区号

movdh,0;DH-磁头号

movdl,[bootdrv];DL-驱动器号

int0x13;读扇区BIOS调用

jncgotoknl;无错误则进入kernel执行

decbyte[retries];读盘计数器减1

jnzreadagain;未到最大读盘次数,则继续读

movsi,loadknlerrmsg;到达最大读盘次数仍然读盘有错,

calldispstr;则显示错误信息

movah,0

int16h;等待用户输入任一键

jmpREBOOTSEG:

REBOOTOFFSET;重新启动

gotoknl:

jmpKERNELSEG:

KERNELOFFSET;跳到内核代码去执行,启动扇区引导程序结束.

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

;字符串显示子程序dispstr

;参数:

si=字符串首地址,字符串以00h作为结束标记

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

dispstr:

pushax

movah,0eh

dispnext:

lodsb

cmpal,0

jzquit

int10h

jmpdispnext

quit:

popax

ret

times510-($-$$)db0;将空闲的程序区域填充为00h

dw0xaa55;由BIOS检测的有效的启动扇区标志

;boot.asm结束

  

内核程序kernel.asm

kernel.asm是系统的核心程序,主要由任务初始化子程序taskinit、进程调度子程序scheduler、键盘中断子程序keybd、16进制数显示子程序printhex及3个参与被调度程序task1~task3等几部分组成,其中进程调度子程序是多任务实现的关键。

内核的工作方式是在系统时钟中断的驱动下,依次使3个进程分别得到调度,使其分时占用CPU完成各自的工作—在屏幕的不同位置分别显示各自的计数器值,即在某一时刻实际只有一个进程在运行,其余的进程都处于挂起状态。

被挂起进程的断点信息,如断点地址、断点处的CPU内的各个寄存器状态,均被保存在各进程的堆栈内。

这样当挂起进程再次被调度运行时,调度程序将断点的信息恢复出来,挂起进程便接着断点继续运行。

内核进程调度如图3所示。

Kernel程序运行时的内存布局图4所示。

图4是图2中的Kernel内存区(80000H~803FF)的放大显示,从图中可见在80000H~8FFFFH的64KB内存区中,低端的1KB区域80000H~803FFH为Kernel的所有程序及数据区。

Kernel程序区部分有用于演示目的的3个进程代码。

当这3个进程被初始化时,分别给每一个进程分配了各自独立的、长度为1KB的3个堆栈区,如图4中的文字标记所示。

分配给进程的堆栈区主要用于保存各自被中断运行时的断点及返回信息。

在进行进程切换时,要进行进程堆栈空间的切换,这些进程堆栈区是实现多任务功能的重要内存区。

Kernel堆栈区位于内存的高端,足以满足程序运行的需要。

Kernel程序运行流程如图5所示。

Kernel在运行时,首先做相关的初始化工作,主要包括数据段、堆栈段寄存器初始化,将其均设定为Kernel的段地址;堆栈指针设在内存的高端;关软盘马达;清屏;初始化3个进程,使其处于运行就绪状态;重新设置定时器及键盘中断向量;而后在时钟中断的驱动下调度程序运行,使3个进程task1~task3分别运行。

任务初始化子程序Taskinit

Taskinit子程序是用来初始化一个进程的,使一个进程成为运行就绪状态。

其所需要的参数有两个:

一个为进程的入口地址,另一个为该进程设定的堆栈指针。

这两个参数分别送入ax及dx寄存器,利用call指令调用该子程序便可初始化一个进程。

在kernel中将进程的可用堆栈区设为绝对地址为80600H以上的内存区,每个进程拥有1KB(400H)的堆栈区,这样第一个进程的堆栈指针就设为80A00H,因为X86CPU堆栈的增长方向是指向低地址的。

如下指令可用来初始化task1:

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

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

movax,task1;进程1入口地址

calltaskinit;初始化task1

进入Taskinit后要完成的工作如下:

1.在Kernel堆栈区保存CPU的通用寄存器ax、cx、bx、dx、SP、bp、si、di及ES和DS。

pusha

pushes

pushds

此时Kernel堆栈区如图6状态。

2.将堆栈指针SP用进程的堆栈指针代替,堆栈指针SP指向进程的堆栈,模拟中断发生时的堆栈操作,在进程的堆栈区先压入标志寄存器,CS及进程的入口地址(在AX中),而后再压入中断发生时要保护的通用寄存器及ES和DS,此时进程堆栈状态如图7所示。

由于在调度程序scheduler中,当其选中了一个将要被调度运行的进程后,它将从SPtable中找到该进程的堆栈指针,并将CPU的堆栈指针寄存器SP设定为该进程的堆栈指针,这样SP便指向了进程的堆栈空间。

在以后恢复现场工作时,将进程堆栈空间内所压入的值恢复到相应的寄存中,最后的一条中断返回指令iret将把进程被中断的断点及标志分别送到IP、CS及标志寄存器中,这样该进程便得以占用CPU,获得运行权,运行自已的程序。

3.将进程堆栈指针SP保存到数据区的SPtable中。

SPtable是一个数组,每个单元2个字节,专用于保存被剥夺运行权的、进程断点处堆栈指针SP的值。

在进程被首次初始化时,其保存有进程序堆栈空间的栈顶地址,按进程初始化的先后顺序依次保存,如表1所示。

表1SPtable保存有进程序堆栈空间栈顶地址

SPtable

SPtable+2

SPtable+4

Task1堆栈指针SP

Task2堆栈指针SP

Task3堆栈指针SP

4.恢复SP为Kernel堆栈区的堆栈指针,将在第1步中保存的各寄存器值出栈,返回到调用程序。

5.各个进程初始化后,各自的堆栈空间均如第2步中所示的状态。

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

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

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2