资源描述
操作系统实验报告+进程调度+作业调度等
———————————————————————————————— 作者:
———————————————————————————————— 日期:
49
个人收集整理 勿做商业用途
操作系统实验报告
1、进程调度
2、作业调度
3、主存空间的分配与回收
4、文件系统
学生学院______计算机学院______
专业班级____网络工程(3)班_____
学 号______3107007062_____
学生姓名________张菲__ _____
指导教师_______胡欣如 _______
2009 年 12 月 20 日
计算机 学院 网络工程 专业 3 班_____组、学号 3107007062
姓名 张菲 协作者 无 教师评定_________________
实验题目 进程调度
一、 实验目的
用高级语言编写和调试一个进程调度程序,以加深对进程的概念及进程调度算法的理解。
二、实验内容和要求
编写并调试一个模拟的进程调度程序,采用“简单时间片轮转法”调度算法对五个进程进行调度.
每个进程有一个进程控制块( PCB)表示.进程控制块可以包含如下信息:进程名、到达时间、需要运行时间、已运行时间、进程状态等等。
进程的到达时间及需要的运行时间可以事先人为地指定(也可以由随机数产生)。进程的到达时间为进程输入的时间. 进程的运行时间以时间片为单位进行计算。
每个进程的状态可以是就绪 W(Wait)、运行R(Run)两种状态之一。
就绪进程获得 CPU后都只能运行一个时间片.用运行时间加1来表示。
如果运行一个时间片后,进程的已占用 CPU时间已达到所需要的运行时间,则撤消该进程,如果运行一个时间片后进程的已占用CPU时间还未达所需要的运行时间,也就是进程还需要继续运行,此时应分配时间片给就绪队列中排在该进程之后的进程,并将它插入就绪队列队尾。 每进行一次调度程序都打印一次运行进程、就绪队列、以及各个进程的 PCB,以便进行检查。
重复以上过程,直到所要进程都完成为止.
三、实验主要仪器设备和材料
硬件环境:IBM-PC或兼容机
软件环境:C语言编程环境
四、实验原理及设计方案
1、进程调度算法:采用多级反馈队列调度算法。其基本思想是:当一个新进程进入内在后,首先将它放入第一个队列的末尾,按FCFS原则排队等待高度。当轮到该进程执行时,如能在该时间片内完成,便可准备撤离系统;如果它在一个时间片结束时尚为完成,调度程序便将该进程转入第二队列的末尾,再同样地按FCFS原则等待调度执行,以此类推。
2、实验步骤:
(1)按先来先服务算法将进程排成就绪队列。
(2)检查所有队列是否为空,若空则退出,否则将队首进程调入执行.
(3)检查该运行进程是否运行完毕,若运行完毕,则撤消进程,否则,将该进程插入到下一个逻辑队列的队尾.
(4)是否再插入新的进程,若是则把它放到第一逻辑队列的列尾。
(5)重复步骤(2)、(3)、(4),直到就绪队列为空。
五、流程图
进程完成,撤消该进程
就绪队列首进程投入运行
时间片到,运行进程已占用CPU时间+1
运行进程已占用CPU时间已达到所需的运行时间
把运行进程插入到下一个队列的队尾
插入新的进程
开始
初始化PCB,输入进程信息
所有队列都为空
各进程按FCFS原则排队等待调度
退出程序
是
是
六、结果过程及截图
初始化队列
进程所在的逻辑队列
P1需要运行两个时间片,本进程才离开此队列
输入所有进程后的进程信息如下:
按Y键继续运行进程:
P1需要运行1个时间片,本进程才离开此队列
按Y键继续运行进程:
P1加入到了第二个逻辑队列
运行若干次后的状态:
队列数越大,进程在次队列可停留的时间就越大
P3运行完毕了
添加新的进程:
七、所遇困难的解决以及心得体会
在这个多级反馈的实验中,我采取了用一条实际上的链表队列来模拟多个逻辑上的队列,通过维护几个链表的状态信息来找到每个进程运行完后应该插入的地方,还有一个标志位Fend用来表明新插入的队列的位置.虽然实验原理很简单,但是在编写代码的过程中遇到了不少的问题,在两个小时之内已经完成的大体代码的编写,但是之中存在不少的问题,导致了用了差不多四个小时的时间去调试才把它弄好,这主要归咎于在开始设计代码的不太合理,在后期使得代码结构有些混乱,使得调试更加的麻烦,以及对编程的不熟悉.通过这个实验不仅使我对进程的调度算法有了更深的认识,使得理论知识得到的实践,也使我的编程能力得到了进一步提高。
七、思考题
1、 分析不同调度算法的调度策略,比较不同调度算法的优缺点,总结它们的适用范围.
答:动态有限权算法:动态优先权是指在创建进程时所创建的优先权,会随进程的推进或者等待时间的增加而改变,以便获得更好的调度性能。处理机为每个进程分配一定的时间片,在就绪队列中,优先权高的进程将优先获得处理机,进程在进去运行完响应的时间片后,如没完成,优先权减1,从新回到就绪队列等待分配处理机。
时间片的轮转法:系统将所有进程排成一个队列,按照先来先服务的原则,对队列首的进程进行处理,每个进程在用完自己的时间片后,从新回到队尾进行排队。每运行一次,进程的需要时间减1,直到就绪队列为空!
八、源代码
#include〈stdio.h〉
#include〈stdlib.h>
#include<conio.h>
#define getpch(type) (type*)malloc(sizeof(type))
#define NULL 0
#define TIME 2//时间片长度
typedef struct pcb{//进程管理块
char name[10];//进程名字
char state; //进程状态
int queue; //进程所在的队列
int ntime; //进程需要运行的时间
int rtime; //进程已经运行的时间
int etime; //进程在本队列可运行的时间片
struct pcb *link;
}PCB;
PCB *ready = NULL, *pinsert = NULL, *pfend = NULL,*p =NULL; //就绪队列,进程插入位置的变量
int geti() //使用户仅能输入整数
{
char ch;
int i = 0;
fflush(stdin);
ch = getchar();
while(ch == ’\n’){
printf(”\tf输入不能为空。。请重新输入\n”);
fflush(stdin);
ch = getchar();
}
while(ch != ’\n'){
if(ch 〉 ’9’ || ch < ’0’){
printf("\t输入有误!!输入只能为正整数,请重新输入。..\n”);
fflush(stdin);
i = 0;
ch = getchar();
}else{
i = i*10 + (ch - '0');
ch = getchar();
}
}
return i;
}
void findpos()//更新状态量
{
PCB *ps = pfend;
if(!ps || !ps —〉 link || (ps—〉 link—>queue — ps->queue) > 1)
pinsert = ps;
else{
while (ps—>link && ps -〉link-〉queue != (pfend ->queue +2))
ps = ps—>link;
pinsert = ps;
}
}
void insert()//插入进程
{
if(!ready ){
ready = p;
pfend = p;
pinsert = p;
}else if(ready —〉queue == 1){//第一队列存在
p—〉link = pfend->link;
pfend->link = p;
pfend = p;
findpos();
}
else{
p—〉link = ready;
ready = p;
findpos();
}
}
void input()/*建立进程控制块函数*/
{
int i,num;
printf("\n请输入进程的个数?");
num = geti();
for(i=0; i < num; i++)
{
printf("\n进程号No。%d:\n”,i+1);
p=getpch(PCB);
printf("\n输入进程名:");
scanf("%s”,p->name);
printf(”\n输入进程运行时间:”);
p —〉ntime = geti();
printf("\n”);
p-〉rtime=0;
p—>state=’w’;
p-〉queue =1;
p->etime = TIME;
p->link=NULL;
insert();/*调用insert函数*/
}
}
void disp(PCB *pr)/*建立进程现实函数,用于显示当前进程*/
{
printf(”\nname\t state\t queue\t ntime\t rtime\t在队列可停留时间\t \n”);
printf("|%s\t”,pr—>name);
printf(” |%c\t",pr—>state);
printf(" |%d\t",pr—〉queue);
printf(” |%d\t”,pr->ntime);
printf(" |%d\t”,pr->rtime);
printf(” |%d\t",pr->etime);
printf("\n”);
}
void check()/*建立进程查看函数*/
{
PCB *pr;
printf("\n ****当前正在运行的进程是:%s",ready->name);/*显示当前运行的进程*/
disp(ready);
pr= ready ->link;
printf(”\n****当前就绪队列状态为:\n");/*显示就绪队列状态*/
while(pr!=NULL)
{
disp(pr);
pr=pr-〉link;
}
}
void sort()//调整进程队列
{
if(!ready->link ||ready—>queue < ready->link—〉queue) return;
p = ready —〉link;
ready ->link = pinsert —〉link;
pinsert ->link = ready;
pinsert = ready;
ready = p;
if (ready && ready -〉 queue == pinsert -〉queue){
findpos();
}
}
void addnew()//添加新的进程
{
if(ready -〉queue != 1){
(ready —〉 queue)++;
ready->etime *= 2;
ready —〉 state='w';
sort();/*调用sort函数*/
input();
}
else{
input();
}
}
void destroy()/*建立进程撤销函数(进程运行结束,撤销进程)*/
{
printf(”\n进程[%s]已完成.\n",ready—〉name);
p = ready;
ready = ready->link;
free(p);
if (ready && ready -〉 queue == pinsert —〉queue)
findpos();
}
void running()/*建立进程就绪函数(进程运行时间到,置就绪状态)*/
{
(ready —〉 rtime)++;
ready -〉etime —-;
if(ready—〉rtime == ready—>ntime){
destroy();
return;
}else if(ready —>etime == 0){
int time = 2;
(ready —> queue)++;
for(int i = 2; i != ready—〉queue; ++i)
time *= 2;
ready—〉etime = time;
ready -〉 state='w';
sort();/*调用sort函数*/
}
}
void main()
{
char ch;
input();
while(ready != NULL)
{
printf("\nThe execute name:%s\n”,ready —〉name);
ready —>state = ’R’;
check();
running();
printf(”\n按i键添加新进程。。。。按其他任意键继续运行...");
fflush(stdin);
ch = getchar();
if (ch == 'i’|| ch=='I')
addnew();
}
printf(”\n\n 进程已经完成\n");
getchar();
}
计算机 学院 网络工程 专业 3 班_____组、学号 3107007062
姓名 张菲 协作者 无 教师评定_________________
实验题目 作业调度
一、实验目的
本实验要求学生模拟作业调度的实现,用高级语言编写和调试一个或多个作业调度的模拟程序,了解作业调度在操作系统中的作用,以加深对作业调度算法的理解。
二、实验内容和要求
1、编写并调度一个多道程序系统的作业调度模拟程序。
作业调度算法:采用基于先来先服务的调度算法.可以参考课本中的方法进行设计。
对于多道程序系统,要假定系统中具有的各种资源及数量、调度作业时必须考虑到每个作业的资源要求。
三、实验主要仪器设备和材料
硬件环境:IBM—PC或兼容机
软件环境:C语言编程环境
四、实验原理及设计方案
采用多道程序设计方法的操作系统,在系统中要经常保留多个运行的作业,以提高系统效率。作业调度从系统已接纳的暂存在输入井中的一批作业中挑选出若干个可运行的作业,并为这些被选中的作业分配所需的系统资源。对被选中运行的作业必须按照它们各自的作业说明书规定的步骤进行控制。
采用先来先服务算法算法模拟设计作业调度程序。
(1)、作业调度程序负责从输入井选择若干个作业进入主存,为它们分配必要的资源,当它们能够被进程调度选中时,就可占用处理器运行。作业调度选择一个作业的必要条件是系统中现有的尚未分配的资源可满足该作业的资源要求。但有时系统中现有的尚未分配的资源既可满足某个作业的要求也可满足其它一些作业的要求,那么,作业调度必须按一定的算法在这些作业中作出选择。先来先服务算法是按照作业进入输入井的先后次序来挑选作业,先进入输入井的作业优先被挑选,当系统中现有的尚未分配的资源不能满足先进入输入井的作业时,那么顺序挑选后面的作业.
(2) 假定某系统可供用户使用的主存空间共100k,并有5台磁带机.
3)流程图:
五、结果过程及截图
读取文件jobs.txt来初始化主存,磁带机的个数,并打印出来。
初始时间是9:00:
按Y运行5分钟:
按Y运行5分钟:
按Y运行5分钟:
多次运行后最后状态:
六、所遇困难的解决以及心得体会
这个实验是花的时间最多的一个实验,第一次做的时候由于理解有些问题,所以做错了.之后重新做了一遍,收获还是很多的,遇到了很多的细节问题,例如像是时间化成浮点数和浮点数化成时间等一些问题,从中也暴露了自己的编程能力欠缺,之后要多多的写程序。
七、思考题
1、 写出每种算法的调度策略,最后比较各种算法的优缺点。
答:先来先服务算法是根据作业的进入时间来排序,到达时间短的先运行,优点是实现简单,缺点是运行时间慢。
短作业优先算法是根椐作业的估计运行时间来排序,估计运行时间短的先运行,优点是运行时间快,缺点是实现起来比较复杂。
2、 选择调度算法的依据是什么?
答:如果作业要求的速度不高,而且作业比较小型,那就最好用先来先服务算法.
如果作业要求的速度高,作业流程复杂,那就最好用短作业优先算法。
八、源代码
#include〈stdio.h>
#include〈stdlib.h〉
#include〈string.h〉
#include<conio。h〉
#define getjcb() (JCB*)malloc(sizeof(JCB))
typedef struct {//资源的总量
int memory;
int tape;
}RESOURCE;
typedef struct JCB {//作业控制块
char username[20];//用户名
char jobname[10];//作业名
char state;//作业状态
char atime[5];//到达时间
float rtime;//运行时间
RESOURCE resource;//资源数量
struct JCB*link;
}JCB;
RESOURCE source = {100,5};
JCB *pjcb =getjcb();//作业链表头
char nowtime[5];//现在时间,初始时间为9:00
FILE* ignore(FILE *fp)//忽略文件中的空白符
{
if(feof(fp)) return fp;
char ch = fgetc(fp);
while (!feof(fp) && (ch == ' '|| ch == ’ ’)){
ch = fgetc(fp);
}
//if(!feof(fp)) return fp;
fseek(fp, —1, SEEK_CUR);
return fp;
}
FILE* findchar(FILE *fp,char c)//在文件中找到一个字符的位置(读取文件时用)
{
if(feof(fp)) return fp;
char ch = fgetc(fp);
while (!feof(fp) && (ch != c)){
ch = fgetc(fp);
}
fseek(fp, -1, SEEK_CUR);
return fp;
}
void destory()//释放链表所占的内存
{
JCB *p = pjcb->link;
while(pjcb){
free(pjcb);
pjcb = p;
if(p)
p = p-〉link;
}
}
float stof(char *time)//把时间转化为浮点型数
{
float h = 0, m = 0;
int i = 0;
while(time[i] != ’:’){
h = h*10 + time[i] - ’0’;
i++;
}
i++;
while(time[i] != '\0'){
m = m*10 + time[i] - '0’;
i++;
}
return (h + m/60);
}
char* ftos(double ftime)//把浮点型数值转化为时间
{
int h,m;
h = int(ftime);
m = int((ftime-h)*60);
sprintf(nowtime,”%c:%c%c",h+’0’,int(m/10)+’0',int(m%10)+’0');
return nowtime;
}
float timesub(char *time1, char *time2)//两个时间相减,得到时间差
{
return stof(time1) — stof(time2);
}
void print()//打印输出
{
JCB *p = pjcb—>link;
printf(”现在时间是%s\n",nowtime);
printf("现在资源的数量%d\t\t%d\n”,source。memory,source。tape);
printf("\t\t\t\t\t\t\t 资源要求\n");
printf(”用户名\t作业名\t状态\t到达时间\t运行时间(小时)\t主存(K)\t磁带机\n”);
while(p){
printf("%s\t%s\t%c\t%s\t\t\t%。2f\t%d\t%d\n”, p—〉username, p—〉jobname, p—>state, p—>atime, p—>rtime, p-〉resource.memory,p—>resource.tape);
p = p-〉link;
}
}
void sendsource()//为作业分配资源
{
JCB *p;
p = pjcb->link;
while(p){//为到达的作业调度
if(p—>state == ’W’ && source.memory - p->resource。memory 〉=0 && source.tape - p-〉resource.tape >=0){
p-〉state = ’R';
source.memory -= p-〉resource.memory;
source。tape —= p—>resource.tape;
printf(”\n%s\t%s被调入内存\n”, p-〉username, p->jobname);
}
p = p-〉link;
}
}
void init()//初始化,读取文件中的作业信息
{
FILE *fp;
JCB *p= NULL,*q = pjcb ;
if((fp = fopen(”jobs.txt”, ”r")) == NULL){
printf(”Cannot open the file!");
exit(1);
}
rewind(fp);
fp = findchar(fp, ’A’);
while (!feof(fp)){
p = getjcb();
fscanf(fp, ”%s",p-〉username);
fp = ignore(fp);
fscanf(fp, "%s",p-〉jobname);
fp = ignore(fp);
fscanf(fp, "%c”,&p—〉state);
fp = ignore(fp);
fscanf(fp, "%s",p—〉atime);
fp = ignore(fp);
p->rtime = 0;//不初始化则会发生错误,?????
fscanf(fp, "%f",&(p—>rtime));
fp = ignore(fp);
fscanf(fp, ”%d”,&p—>resource。memory);
fp = ignore(fp);
fscanf(fp, ”%d”,&p-〉resource.tape);
fp = ignore(fp);
q—>link = p;
q = p;
}
p ->link = NULL;
sendsource();
fclose(fp);
}
int checkend() //检查是否所有的作业都已经运行完了
{
JCB *p = pjcb -〉link;
while(p){
if(p —〉state != 'F'){
return 0;
}
p = p—>link;
}
return 1;
}
void run()//运行作业
{
if(checkend()){//检查是否所有的作业都已经运行完了
printf("所有作业都已经完成..。\n");
exit(0);
}
JCB *p = pjcb ->link;
double time;
while(p){//作业运行完毕释放资源
time = stof(nowtime) - stof(p—〉atime);
if(p ->state == 'R' && time 〉= p—>rtime){
p-〉state = 'F';
source.memory += p-〉resource.memory;
source.tape += p->resource。tape;
printf("\n%s\t%s已经执行结束\n", p-〉username, p->jobname);
break;
}
p = p—>link;
}
p = pjcb —〉link;
while(p){//计算到达的作业
if( strcmp(nowtime, p->atime) ==0 && p->state == ’N’){
p—>state = ’W’;
printf("\n%s\t%s作业已到达\n”, p-〉username, p-〉jobname);
}
p = p—〉link;
}
sendsource();//为作业分配资源
print();
}
int main()
{
char ch;
double time =9.00;
double step = float(5)/60+0。00001;
ftos(9。0);
init();
do{
run();
puts(”**************************************************\n”);
puts("是否继续运行,每次运行5分钟Y/N。.。。”);
puts(”**************************************************\n”);
ch = getche();
time += step;
ftos(time);
}while(toupper(ch) == 'Y’);
destory();
return 0;
}
计算机 学院 网络工程 专业 3 班_____组、学号 3107007062
姓名 张菲 协作者 无 教师评定_________________
实验题目 主存空间的分配和回收
一、实验目的
熟悉主存的分配与回收。理解在不同的存储管理方式下,如何实现主存空间的分配与回收。掌握动态分区分配方式中的数据结构和分配算法及动态分区存储管理方式及其实现过程.
二、实验内容和要求
主存的分配和回收的实现是与主存储器的管理方式有关的。所谓分配,就是解决多道作业或多进程如何共享主存空间的问题。所谓回收,就是当作业运行完成时将作业或进程所占的主存空间归还给系统。
可变分区管理是指在处理作业过程中建立分区,使分区大小正好适合作业的需求,并且分区个数是可以调整的。当要装入一个作业时,根据作业需要的主存量查看是否有足够的空闲空间,若有,则按需要量分割一个分区分配给该作业;若无,则作业不能装入,作业等待。随着作业的装入、完成,主存空间被分成许多大大小小的分区,有的分区被作业占用,而有的分区是空闲的。
实验要求使用可变分区存储管理方式,分区分配中所用的数据结构采用空闲分区表和空闲分区链来进行,分区分配中所用的算法采用首次适应算法、循环首次适应算法、最佳适应算法三种算法来实现主存的分配与回收。同时,要求设计一个实用友好的用户界面,并显示分配与回收的过程。
三、实验主要仪器设备和材料
硬件环境:IBM-PC或兼容机
软件环境:VC++ 6。0
四、实验原理及设计方案
1、循环首次适应算法
在该算法中,把主存中所有空闲区按其物理地址递增的次序排列。在为作业分配存储空间时,从上次找到的空闲分区的下一个空闲分区开始查找,直到找到第一个能满足要求的空闲区,从中划出与请求的大小相等的存储空间分配给作业,余下的空闲区仍留在空闲区表或链中。
2、 实验步骤
(1)初始化空闲分区;
(2)反复对现有的空闲分区进行进程创建和撤消,即内存分配和回收;
(3)退出.
3、流程图
输入要释放内存的基地址和大小
五、结果过程及截图
初始化主存大小后的状态
按1后分配一块内存:
按1后分配一块内存:
按2释放内存:
六、所遇困难的解决以及心得体会
本实验我采取用一条链表同时表示空闲分区链和主存空间占用情况,因为主存总大小是固定的,把空闲分区链所表示的区域从总的内存里去除就是被占用的空间的大小,这个实验还是比较简单的,因此很快就完成了,通过它使我学习了主存空间的分配与回收,同时也让我意识到了在回收内存时的一些问题,使我对课本知识有了更进一步的理解.
七、思考题
1、内存的主要分配方式有哪些?回收时可能出现的什么情况?应怎样处理这些情况?
答:有定分区分配和动态分区分配两种,回收时可能出现内存分区被切成若干在小不等小分区,过小的分区浪费内存资源,这要求我们要用紧凑技术修整.
2、动态分区管理的常用内存分配算法有哪几种?比较它们各自的使用范围。
答:有首次适应算法、循环首次适应算法、最佳适应算法三种.
首次适应算法适用于小型作业,而且分配速度不怎么要求的作业,循环首次适应算法适用于一些大的作业,避免大作业长期得不到分配,最佳适应算法适应于对分配速度要求高,作业容量比较大的作业。
八、源代码
#include <stdio。h〉
#include 〈malloc。h〉
#include〈conio。h〉
#define getMEM() (MEM*)(malloc(sizeof(MEM)))
typedef struct Memory{//可用分区链表节点
int base;//基地址
int size;//大小
struct Memory *next;
}MEM;
MEM *pm = getMEM();//可用分区的链表头
MEM *temp;//
int SIZE;//总的内存的大小,由用户输入
int geti()//让用户只能输入正整数
{
char ch;
int i = 0;
fflush(stdin);
ch = getchar();
while(ch == '\n’){
printf(”\tf输入不能为空.。请重新输入\n");
fflush(stdin);
ch = getchar();
}
while(ch != ’\n’){
if(ch 〉 '9' || ch 〈 ’0'){
printf("\t输入有误!!输入只能为正整数,请重新输入.。。\n");
fflush(stdin);
i = 0;
ch = getchar();
}else{
i = i*10 + (ch — '0’);
ch = getchar();
}
}
return i;
}
bool check(MEM* pm, int base, int size){//检查用户输入的合法性
MEM *p = pm;
p = pm;
while(p—〉next){
if(p->base + p-〉size 〈= base && base 〈= p—〉next->base && p->next-〉base >= base + size){
return true;
}
p= p —>next;
}
if(!p—〉next && p-〉base + p—>size 〈 SIZE){
if(p—>base + p-〉size <= base && base <= SIZE && SIZE 〉= base + size)
return true;
}
return false;
}
bool release(MEM *pm, int base, int size){//释放内存空间
MEM *p = pm;
if(!check(pm, base ,size)){
return false;
}
while(p—>next){
if(base + size <= p-〉next-〉base)
break;
p = p-〉next;
}
if(base == p—>base + p—〉size){//低地址相邻
if(p —〉next && p->next—〉base == base + size){//释放区上下都相邻
p—〉s
展开阅读全文