操作系统课程方案文档及代码.docx
- 文档编号:25660852
- 上传时间:2023-06-11
- 格式:DOCX
- 页数:20
- 大小:51.46KB
操作系统课程方案文档及代码.docx
《操作系统课程方案文档及代码.docx》由会员分享,可在线阅读,更多相关《操作系统课程方案文档及代码.docx(20页珍藏版)》请在冰豆网上搜索。
操作系统课程方案文档及代码
1设计目的与内容
1.1设计目的
通过课程设计,加深对操作系统对程序执行的理解,掌握操作系统的多程序运行原理,能模拟操作系统设计相应的进程调度算法,掌握操作系统的基本原理及功能,具有初步分析实际操作系统、设计、构造和开发现代操作系统的基本能力。
1.2设计内容
1、设计进程控制块PCB表结构,分别适用于可强占的优先数调度算法和循环轮转调度算法。
2、建立进程就绪队列。
对两种不同算法编制入链子程序。
3、编制两种进程调度算法:
1)可强占的优先进程调度;2)循环时间片轮转调度
4、设计操作系统运行的指令。
2设计说明
2.1需求分析
设计虚拟内核实现进程的调度,实现多道程序的调度。
设计调度算法计算各个进程的优先权限来确定进程执行的次序。
进程调度程序选择一个就绪状态的进程,使之在处理器上运行。
进程的调度采用最高优先数优先的调度算法和先来先服务调度算法相结合的算法,并且采用动态优先数策略,选择进程占用处理器后该进程仅能使用一个时间片,运行完后优先数减1。
2.2设计思路
本程序用两种算法对多个进程进行调度,每个进程可有三个状态,并假设初始状态为就绪状态。
为了便于处理,程序中的某进程运行时间以时间片为单位计算。
各进程的优先数或轮转时间数以及进程需运行的时间片数的初始值均由用户给定。
在优先数算法中,优先数的值为31与运行时间的差值。
进程每执行一次,优先数减1,CPU时间片数加1,进程还需要的时间片数减1。
在轮转算法中,采用固定时间片<即:
每执行一次进程,该进程的执行时间片数为已执行了1个单位),这时,CPU时间片数加1,进程还需要的时间片数减1,并排列到就绪队列的尾上。
设计程序指令,
MOVn//把整数n赋给累加器A
SAVm//把累加器A的值存入地址M
ADDn//从累加器A的值减去整数n,结果送到累加器A。
SUBn//从累加器A的值减去整数n,结果送到累加器A。
MULn//从累加器A的值乘以整数n,结果送到累加器A。
DIVn//从累加器A的值除以整数n,结果送到累加器A。
JEQm//F为0跳转到m
JLGm//F大于0跳转到m
JLEm//F大于等于0跳转到m
JMPm//无条件跳转到m
OUTport//累加器的内容输出到端口port。
port为0,指显示器
2.3算法和流程图
定义一个PCB数据结构:
structPCB{
intA。
//累加器A
intPC。
//程序计数器PC
char*addr。
//程序加载起始地址
intlength。
//程序大小
intruntime。
intwaittime。
intstate。
intpname。
intpri。
//优先级数
structPCB*next。
}pcbs[MAX_PID]。
/*运行指针*/
进程调度算法如下:
while(!
apd(>>
{
while(pcbs[cur_pid].state==05||pcbs[cur_pid].priority
cur_pid=(cur_pid+1>%max_pid。
exeInstruction(cur_pid>。
//执行指令
PCB*pcb=pcbs+cur_pid。
//获取当前进程PCB
tick++。
if(pcb->PC>=pcb->length>//执行完毕
{
delete[]pcb->addr。
//回收内存
pcb->priority=0。
//优先级置零
pri=maxpri(>。
pcb->state=05。
//将已经完成了的pcb状态设置为05已完成
}
if(pcb->last>=pcb->slice>{
pcb->last=0。
cur_pid=(cur_pid+1>%max_pid。
}
可强占的优先进程调度,开始建立一个就绪队列,首先检查是否还有进程没有完毕,如果没有时间片就加1,从就绪队列中查找一个优先级最高的进程,如果在设定的时间片内没有完成就将此进程放入就绪队列中的后面,并将优先级减1。
并循环检查是否还有进程没有完成。
可强占优先调度算法实现过程流程图,如图2.1:
图2.1可强占优先调度算法实现过程流程图
循环轮转算法实现过程,开始建立一个就绪队列,首先检查是否还有进程没有完毕,如果没有时间片就加1,根据进程的时间片的个数运行进程。
并循环检查是否还有进程没有完成。
循环轮转算法实现过程流程图,如图2.2:
图2.2循环轮转算法实现过程流程图
2.4测试
2.4.1执行多进程同时执行同一个存在的程序。
两个进程的结果一样。
同时执行两个存在的程序。
两个进程的结果正确。
同时执行3个存在的程序。
3个进程的结果正确。
同时执行10个进程。
每个进程结果正确。
同时执行10个以上的进程。
输出错误信息。
输入程序名,并在后面加入要运行的用户程序,并输入初始化优先级。
2.4.2程序运行结果
运行程序的优先级相同时的结果如图2.3
图2.3优先级相同时的结果图
运行程序的优先级不相同时,高优先级运行结束后低优先级的再运行,结果如图2.4
图2.4优先级不相同时的结果图
3设计总结
经过这次程序设计,我从中收获良多。
由于是第一次接触操作系统的课程设计,在完成过程中也遇到了不少困难,本次的编程语言也选用的C++,由于最近几年都是在用java编程,对于C++也略显生疏了。
在这次的实验中明显认识到要想设计出好的东西,必须要一步一步脚踏实地的去学习和了解透彻每一个知识点,这是关键,不然基本的东西都没弄清楚就盲目下手,只会对后面的设计造成不必要的麻烦,而使前面的功夫都功亏一篑,这是不值得的。
每一次设计过程中,都要先弄懂此次设计的目的,所要实现的功能,并知道运用正确的算法来实现。
这个过程是需要一定时间的,不是急于求成能出来的。
算法可以说是编程的生命,没有一个好的算法,程序就如一个没有灵魂的人,犹如枯木,没有生机,所以算法是关键。
在实现本设计过程中选用的是数组来保存进程,其实后来有想过用指针其实更加利用后面算法的进行,只是因为开始所使用的数组,烦与后面的更改,就还是最终选用的数组来进行这次的设计,数组的一个好处是可以非常容易的通过下标来得到当前进程,这点是比较易于代码的编写。
在基本算法想好后,后面的代码的实现就比较简单了,因为有了大一的C++基础,所以编写起来也没有什么太大的困难,总体的框架建立起来后,便是解决一些参数的调用的问题,有时候会因为一个小的参数初始化问题,或是与其他参数弄混的问题,是在调试过程中出现许多的问题。
虽说是小问题,但是就是这种小问题是最难发现的,只有通过自己手动将程序在脑海中运行一遍,仔细检查每个参数是否传递正确,才能真正的检查出错误的所在,在这个时候最忌讳的就是浮躁,最需要的是沉着,能真正仔细的检查,不是粗略的检查,而是将每一个函数的算法都过一遍,这样才有可能找到真正出现问题的地方,在测试过程中可通过Cout来输出有关信息来观察出错位置。
在实现多个进程并行工作并结合调度算法的实现的调试过程中出现不少问题,首先在交替执行后,一个进程执行完后,怎样让另一个进程继续执行是这个过程中遇到的一个问题,后来可通过一个count计数来解决,并利用while循环判断是否还有进程没有执行,从而继续执行。
在并行执行过程中出现两个可以并行,有时三个也可以并行,但到第四个并行过程中出现问题,在每个函数中依次输出相关信息来验证出错地点时,发现是在ArrayPriority(>函数中出错,返回的是进程的pid而不是下标,而本程序设计中需要用得到的是最大优先级进程的下标,所以出现随机性的错误。
另一个调试过程中出现的错误,也是与上面的随机性可并行的结果有关,在for循环中进行比较时,下标的范围应该是i
在基本算法正确的情况下,容易被一些小问题所困扰,也是比较不容易发现的问题,就必须通过耐心的调试来得到解决。
总之,在本次课程设计中,我不仅掌握了虚拟内核的建立方法,还知道了怎样通过编程来实现并行运行和基于优先级的时间片调度算法。
这些是以前都没想过的,以前认为这些东西都太过于抽象,没有实际的意义,但是通过课程设计,让我更加具体的了解到了操作系统的神奇之处,也更加想去深入了解到更多的知识,这也是有通过更多的自我学习来实现的,而我会继续努力学习并将理论联系实际去实现下去。
参考文献
[1]AbrahamSilberschatz.操作系统概念(第8版影印版>.北京:
高等教育出版社,2018.
[2]GaryNutt(著>,潘登(译>.Linux操作系统内核实习.北京:
机械工业出版社,2004.
[3]于渊.自己动手写操作系统<影印版).北京:
电子工业出版社,2005
附录A使用说明
虚拟内核编译说明
目标一:
编译“编译程序cpl”
操作步骤:
1.进入cpl文件夹
2.双击cpl.dsw打开工程
3.单击菜单【组建】【批组建】,勾选【cpl-Win32Release】,单击【创建】
4.把Release文件夹里的cpl.exe复制到“测试”文件夹。
目标二:
编译“编译程序vknl”
操作步骤:
1.进入vknl文件夹
2.双击vknl.dsw打开工程
3.单击菜单【组建】【批组建】,勾选【vknl-Win32Release】,单击【创建】
4.把Release文件夹里的vknl.exe复制到“测试”文件夹。
虚拟内核测试操作说明
目标一:
书写源程序
用虚拟指令写源程序,计算1+2+3+4+5+6+7+8+9+10
我已经写好了,程序名为app1.prg,放在“测试”文件夹下,你可以用记事本打开看看。
目标二:
编译程序
操作步骤:
Windows下运行cmd,然后进入“测试”目录。
<如果你不会,就双击do.bat)
cplapp1.prgapp1.bin
目标三:
启动内核
操作步骤:
vknlapp1.bin
附录B程序源代码
程序源代码:
1、程序头文件
#ifndef__VIRTUAL_KERNEL__
#define__VIRTUAL_KERNEL__
#include"iostream.h"
#include"string.h"
#include"stdio.h"
#include"time.h"
#include
#defineNOP00//空指令:
消耗一个机器周期
#defineMOV01//传送指令:
立即数赋值给寄存器A
#defineADD02//加法指令:
寄存器A加立即数
#defineOUT80//输出指令:
寄存器A的值输出到端口(端口号01--表示显示器>
#defineMAX_PID10//系统并发运行的进程数的最大值
//定义PCB结构类型
structPCB{
intA。
//累加器A
intPC。
//程序计数器PC
char*addr。
//程序加载起始地址
intlength。
//程序大小
intruntime。
intwaittime。
intstate。
intpname。
intpri。
//优先级数
structPCB*next。
}pcbs[MAX_PID]。
/*运行指针*/
structPCB*running。
/*高优先级就绪队列头指针*/
structPCB*Hready。
/*低优先级队列头指针*/
structPCB*Lready。
/*等待队列头指针*/
structPCB*wait。
intsig=0。
intcur_pid。
//当前_进程号
intreadyNum=MAX_PID。
/**************************以下是函数说明****************************/
/*利用循环实现延迟*/
voiddelay(>。
voidproc(structPCB*running>。
/*将node插入到head所指示的队列的尾部*/
voidInsertIntoQueueTail(structPCB**head,structPCB*node>。
/*进程调度函数*/
intproc_switch(>。
/*进程等待函数*/
voidproc_wait(>。
/*进程唤醒函数*/
intproc_wakeup(>。
voidinitSystem(void>。
//初始化系统
intloadProgram(char*name>。
//加载应用程序
intexeInstruction(intpid>。
//执行指令
voidremoveFromQueue(structPCB**head,structPCB*node>。
#endif
2、程序文件
#include"vknl.h"
intmain(intargc,char*argv[]>
{
inti。
if(argc!
=2>argv[1]="app1.bin"。
//默认应用程序
initSystem(>。
for(i=0。
i i++>{ loadProgram(argv[i]>。 //加载应用程序 } /*模拟进程调度开始*/ for(。 readyNum>0。 > { switch(sig>{ case0: /*无进程等待调度,打印信息并返回*/ if(! proc_switch(>> { printf("NoProcesstorun,pressanykeytoreturn: \n">。 getchar(>。 exit(-1>。 } break。 case1: proc_wait(>。 break。 case2: case3: case4: case5: case6: case7: case8: case9: case10: case11: proc(running>。 break。 default: printf("\nerror! ">。 exit(-1>。 } } return0。 //退出虚拟内核 } //初始化系统 voidinitSystem(void> { cur_pid=0。 /*等待队列和高优先级队列为空*/ wait=NULL。 Hready=NULL。 } //加载应用程序 intloadProgram(char*name> { FILE*fin=fopen(name,"rb">。 //打开源程序文件 if(fin==NULL>return-1。 //若打开失败,返回-1 fseek(fin,0L,SEEK_END>。 //文件读写指针移到文件末尾 longfsize=ftell(fin>。 //求文件长度 fseek(fin,0L,SEEK_SET>。 //读写指针移到文件开头 PCB*pcb=pcbs+cur_pid。 //获取当前进程PCB pcb->addr=newchar[fsize]。 //为新进程分配内存 fread(pcb->addr,fsize,1,fin>。 //程序读入内存 fclose(fin>。 //关闭文件 pcb->length=fsize。 //进程大小 pcb->A=0。 //初始化寄存器 pcb->PC=0。 pcb->pname=(cur_pid+2>。 pcb->waittime=0。 pcb->state=1。 InsertIntoQueueTail(&Hready,pcb>。 //&pcb[i] cur_pid++。 return0。 //正常返回 } //执行指令 intexeInstruction(intpid> { PCB*pcb=pcbs+(pid-2>。 //获取当前进程PCB charop_cmd=*(pcb->addr+pcb->PC>。 //取操作码 shortop_dat=*((short*>(pcb->addr+pcb->PC+1>>。 //取操作数 //注意,我们约定操作数是16位整数,所以要定义为short。 if(op_cmd==MOV>//传送指令 { pcb->A=op_dat。 //操作数赋给累加器A } elseif(op_cmd==ADD>//加法指令 { pcb->A+=op_dat。 //加计算 } elseif(op_cmd==SUB> { pcb->A-=op_dat。 } elseif(op_cmd==MUL> { pcb->A*=op_dat。 } elseif(op_cmd==DIV> { pcb->A/=op_dat。 } elseif(op_cmd==MOD> { pcb->A%=op_dat。 } elseif(op_cmd==JMP> { printf("JMPoperation.jumpto%d\n",op_dat>。 pcb->pc+=(op_dat*3>。 } elseif(op_cmd==OUT>//输出指令 { printf("OUToperation.resultis%d\n",pcb->A>。 //输出累加器A的内容 } pcb->PC+=3。 //修改程序计数器 if(pcb->PC>=pcb->length> { readyNum--。 pcb->state=0。 sig=0。 } return0。 //正常返回 } /*功能: 进程*/ /*入口参数: 运行指针*/ /*出口参数: 无*/ voidproc(structPCB*running> { inti。 if(running->state==0>return。 /*显示当前运行的进程的id*/ printf("\nNowProcess%disrunning\n",running->pname>。 /*当前进程执行running->runtime个时间片*/ for(i=running->runtime。 i>0。 i-->{ exeInstruction(running->pname>。 /*显示剩余的时间片*/ printf("%dtimeslice(s>left\n",i>。 /*延迟*/ delay(>。 proc_wakeup(>。 } /*产生一个1到1000的随机数,若该随机数小余100,当前进程等待,*/ if((rand(>%10000+1><1000>{ printf("Process%dbeginstowait.\n",running->pname>。 sig=1。 return。 } /*显示时间片耗尽,进程转为低优先级就绪状态*/ printf("Timeslicesforprocess%dexhausted.\n",running->pname>。 InsertIntoQueueTail(&Lready,running>。 sig=0。 return。 } /*功能: 将一个节点插入队列尾部*/ /*入口参数: 队列头指针地址head,待插入结点node*/ /*出口参数: 无*/ voidInsertIntoQueueTail(structPCB**head,structPCB*node> { structPCB*p。 node->next=NULL。 if(*head==NULL>{ /*被插入队列为空*/ *head=node。 return。 } /*被插入队列不为空*/ else{ p=*head。 /*找到队列的最后一个结点*/ while(p->next! =NULL>p=p->next。 p->next=node。 } } /*功能: 进程调度*/ /*入口参数: 无*/ /*出口参数: 若调度成功,返回1,否则返回0*/ intproc_switch(> { /*若高优先级就绪队列和低优先级就绪队列均为空,则循环执行进程唤醒*/ while(Hready==NULL&&Lready==NULL> if(! proc_wakeup(>>return0。 /*若高优先级就绪队列非空,则执行其第一个进程,分配2个时间片*/ if(Hready! =NULL>{ running=Hready。 Hready=Hready->next。 running->runtime=2。 } /*若高优先级就绪队列为空,则执行低优先级就绪队列的第一个进程, 分配5个时间片*/ else{ running=Lready。 Lready=Lready->next。 running->runtime=5。 } /*别调度进程的id赋给sig*/ sig=running->pname。 return1。 } /*功能: 进程等待。 将当前运行进程置高优先级,等待时间为20, 插入等待队列尾部*/ /*入口参数: 无*/ /*出口参数: 无*/ voidproc_wait(> { PCB*p。 running->pri=1。 running->waittime=20。 InsertIntoQueueTail(&wait,running>。 sig=0。 return。 } /*功能: 进程唤醒*/
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 操作系统 课程 方案 文档 代码
![提示](https://static.bdocx.com/images/bang_tan.gif)