操作系统实验报告+进程调度+作业调度等
操作系统实验报告
1、进程调度
2、作业调度
3、主存空间的分配与回收 4、文件系统
学生学院______计算机学院______ 专业班级____网络工程(3)班_____ 学 号______3107007062_____ 学生姓名________张菲__ _____ 指导教师_______胡欣如 _______
2009 年 12 月 20 日
班_____ 姓名 张菲 协作者 无 教师评定_________________ 实验题目 进程调度
一、实验目的
用高级语言编写和调试一个进程调度程序,以加深对进程的概念及进程调度算法的理解。
二、实验内容和要求
编写并调试一个模拟的进程调度程序,采用“简单时间片轮转法”调度算法对五个进程进行调度。
每个进程有一个进程控制块( 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),直到就绪队列为空。
五、流程图
六、结果过程及截图 初始化队列
按Y键继续运行进程:
按Y键继续运行进程:
运行若干次后的状态:
添加新的进程:
七、所遇困难的解决以及心得体会
在这个多级反馈的实验中,我采取了用一条实际上的链表队列来模拟多个逻辑上的队列,通过维护几个链表的状态信息来找到每个进程运行完后应该插入的地方,还有一个标志位Fend用来表明新插入的队列的位置。虽然实验原理很简单,但是在编写代码的过程中遇到了不少的问题,在两个小时之内已经完成的大体代码的编写,但是之中存在不少的问题,导致了用了差不多四个小时的时间去调试才把它弄好,
这主要归咎于在开始设计代码的不太
合理,在后期使得代码结构有些混乱,使得调试更加的麻烦,以及对编程的不熟悉。通过这个实验不仅使我对进程的调度算法有了更深的认识,使得理论知识得到的实践,也使我的编程能力得到了进一步提高。 七、思考题
1、 分析不同调度算法的调度策略,比较不同调度算法的优缺点,总结它们的适用范围。 答:动态有限权算法:动态优先权是指在创建进程时所创建的优先权,会随进程的推进或者等待时间的增加而改变,以便获得更好的调度性能。处理机为每个进程分配一定的时间片,在就绪队列中,优先权高的进程将优先获得处理机,进程在进去运行完响应的时间片后,如没完成,优先权减1,从新回到就绪队列等待分配处理机。
时间片的轮转法:系统将所有进程排成一个队列,按照先来先服务的原则,对队列首的进程进行处理,每个进程在用完自己的时间片后,从新回到队尾进行排队。每运行一次,进程的需要时间减1,直到就绪队列为空! 八、源代码
#include #include
#include
#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(
fflush(stdin); ch = getchar(); }
while(ch != '\n'){
if(ch > '9' || ch
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();
}
void input()/*建立进程控制块函数*/ {
int i,num;
printf(
for(i=0; i
p=getpch(PCB);
printf(
printf(
p->queue =1;
p->etime = TIME; p->link=NULL;
insert();/*调用insert函数*/ } else{ p->link = ready; ready = p; }
findpos();
}
void disp(PCB *pr)/*建立进程现实函数,用于显示当前进程*/ {
printf(
}
void check()/*建立进程查看函数*/
{
}
void sort()//调整进程队列
{
} if(!ready->link ||ready->queue link->queue) return; p = ready ->link; ready ->link = pinsert ->link; pinsert ->link = ready; pinsert = ready; ready = p; if (ready && ready -> queue == pinsert ->queue){ findpos(); } PCB *pr; printf(
void addnew()//添加新的进程
{
if(ready ->queue != 1){ (ready -> queue)++; ready->etime *= 2; ready -> state='w'; sort();/*调用sort函数*/ input(); } else{ input();
}
}
void destroy()/*建立进程撤销函数(进程运行结束,撤销进程)*/
{
}
void running()/*建立进程就绪函数(进程运行时间到,置就绪状态)*/
{
}
void main()
{
char ch; input(); while(ready != NULL) { printf(
}
ready ->state = 'R'; check(); running(); printf(
计算机 学院 网络工程 专业班_____ 姓名 张菲 协作者 无 教师评定_________________ 实验题目 作业调度
一、实验目的
本实验要求学生模拟作业调度的实现,用高级语言编写和调试一个或多个作业调度的模拟程序,了解作业调度在操作系统中的作用,以加深对作业调度算法的理解。
二、实验内容和要求
1、编写并调度一个多道程序系统的作业调度模拟程序。
作业调度算法:采用基于先来先服务的调度算法。可以参考课本中的方法进行设计。 对于多道程序系统,要假定系统中具有的各种资源及数量、调度作业时必须考虑到每个作业的资源要求。
三、实验主要仪器设备和材料
硬件环境:IBM-PC或兼容机
软件环境:C语言编程环境
四、实验原理及设计方案
采用多道程序设计方法的操作系统,在系统中要经常保留多个运行的作业,以提高系统效率。作业调度从系统已接纳的暂存在输入井中的一批作业中挑选出若干个可运行的作业,并为这些被选中的作业分配所需的系统资源。对被选中运行的作业必须按照它们各自的作业说明书规定的步骤进行控制。
采用先来先服务算法算法模拟设计作业调度程序。
(1)、作业调度程序负责从输入井选择若干个作业进入主存,为它们分配必要的资源,当它们能够被进程调度选中时,就可占用处理器运行。作业调度选择一个作业的必要条件是系统中现有的尚未分配的资源可满足该作业的资源要求。但有时系统中现有的尚未分配的资源既可满足某个作业的要求也可满足其它一些作业的要求,那么,作业调度必须按一定的算法在这些作业中作出选择。先来先服务算法是按照作业进入输入井的先后次序来挑选作业,先进入输入井的作业优先被挑选,当系统中现有的尚未分配的资源不能满足先进入输入井的作业时,那么顺序挑选后面的作业。
(2) 假定某系统可供用户使用的主存空间共100k,并有5台磁带机。
3)流程图:
五、结果过程及截图
读取文件jobs.txt来初始化主存,磁带机的个数,并打印出来。
初始时间是9:00:
按Y运行5分钟:
按Y运行5分钟:
按Y运行5分钟:
多次运行后最后状态:
六、所遇困难的解决以及心得体会
这个实验是花的时间最多的一个实验,第一次做的时候由于理解有些问题,所以做错了。之后重新做了一遍,收获还是很多的,遇到了很多的细节问题,例如像是时间化成浮点数和浮点数化成时间等一些问题,从中也暴露了自己的编程能力欠缺,之后要多多的写程序。
七、思考题
1、 写出每种算法的调度策略,最后比较各种算法的优缺点。
答:先来先服务算法是根据作业的进入时间来排序,到达时间短的先运行,优点是实现简单,缺点是运行时间慢。
短作业优先算法是根椐作业的估计运行时间来排序,估计运行时间短的先运行,优点是运行时间快,缺点是实现起来比较复杂。
2、 选择调度算法的依据是什么?
答:如果作业要求的速度不高,而且作业比较小型,那就最好用先来先服务算法。
如果作业要求的速度高,作业流程复杂,那就最好用短作业优先算法。
八、源代码
#include
#include
#include
#include
#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++; while(time[i] != '\0'){ m = m*10 + time[i] - '0'; } i++; i++;
return (h + m/60);
}
char* ftos(double ftime)//把浮点型数值转化为时间
{
}
float timesub(char *time1, char *time2)//两个时间相减,得到时间差
{
}
void print()//打印输出
{
JCB *p = pjcb->link; printf(
p->atime, p->rtime, p->resource.memory,p->resource.tape);
}
void sendsource()//为作业分配资源
{
JCB *p; p = pjcb->link; while(p){//为到达的作业调度 p = p->link; }
if(p->state == 'W' && source.memory - p->resource.memory >=0 && source.tape - p->resource.tape >=0){
} } p = p->link; p->state = 'R'; source.memory -= p->resource.memory; source.tape -= p->resource.tape; printf(
}
void init()//初始化,读取文件中的作业信息
{
FILE *fp; JCB *p= NULL,*q = pjcb ; if((fp = fopen(
} } fp = ignore(fp); p->rtime = 0;//不初始化则会发生错误,????? fscanf(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(
}
} p = p->link; } p = pjcb ->link; while(p){//计算到达的作业 if( strcmp(nowtime, p->atime) ==0 && p->state == 'N'){ p->state = 'W'; printf(
int main()
{
char ch;
double time =9.00;
}
double step = float(5)/60+0.00001; ftos(9.0); init(); do{ run(); puts(
班_____ 姓名 张菲 协作者 无 教师评定_________________
实验题目 主存空间的分配和回收
一、实验目的
熟悉主存的分配与回收。理解在不同的存储管理方式下,如何实现主存空间的分配与回收。掌握动态分区分配方式中的数据结构和分配算法及动态分区存储管理方式及其实现过程。
二、实验内容和要求
主存的分配和回收的实现是与主存储器的管理方式有关的。所谓分配,就是解决多道作业或多进程如何共享主存空间的问题。所谓回收,就是当作业运行完成时将作业或进程所占的主存空间归还给系统。
可变分区管理是指在处理作业过程中建立分区,使分区大小正好适合作业的需求,并且分区个数是可以调整的。当要装入一个作业时,根据作业需要的主存量查看是否有足够的空闲空间,若有,则按需要量分割一个分区分配给该作业;若无,则作业不能装入,作业等待。随着作业的装入、完成,主存空间被分成许多大大小小的分区,有的分区被作业占用,而有的分区是空闲的。
实验要求使用可变分区存储管理方式,分区分配中所用的数据结构采用空闲分区表和空闲分区链来进行,分区分配中所用的算法采用首次适应算法、循环首次适应算法、最佳适应算法三种算法来实现主存的分配与回收。同时,要求设计一个实用友好的用户界面,并显示分配与回收的过程。
三、实验主要仪器设备和材料
硬件环境:IBM-PC或兼容机
软件环境:VC++ 6.0
四、实验原理及设计方案
1、循环首次适应算法
在该算法中,把主存中所有空闲区按其物理地址递增的次序排列。在为作业分配存储空间时,从上次找到的空闲分区的下一个空闲分区开始查找,直到找到第一个能满足要求的空闲区,从中划出与请求的大小相等的存储空间分配给作业,余下的空闲区仍留在空闲区表或链中。
2、 实验步骤
(1)初始化空闲分区;
(2)反复对现有的空闲分区进行进程创建和撤消,即内存分配和回收;
(3)退出。
3、流程图
五、结果过程及截图
初始化主存大小后的状态
按1后分配一块内存:
按1后分配一块内存:
按2释放内存:
六、所遇困难的解决以及心得体会
本实验我采取用一条链表同时表示空闲分区链和主存空间占用情况,因为主存总大小是固定的,把空闲分区链所表示的区域从总的内存里去除就是被占用的空间的大小,这个实验还是比较简单的,因此很快就完成了,通过它使我学习了主存空间的分配与回收,同时也让我意识到了在回收内存时的一些问题,使我对课本知识有了更进一步的理解。
七、思考题
1、内存的主要分配方式有哪些?回收时可能出现的什么情况?应怎样处理这些情况? 答:有定分区分配和动态分区分配两种,回收时可能出现内存分区被切成若干在小不等小分
区,过小的分区浪费内存资源,这要求我们要用紧凑技术修整。
2、动态分区管理的常用内存分配算法有哪几种?比较它们各自的使用范围。
答:有首次适应算法、循环首次适应算法、最佳适应算法三种。
首次适应算法适用于小型作业,而且分配速度不怎么要求的作业,循环首次适应算法适用于一些大的作业,避免大作业长期得不到分配,最佳适应算法适应于对分配速度要求高,作业容量比较大的作业。
八、源代码
#include
#include
#include
#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(
}
bool check(MEM* pm, int base, int size){//检查用户输入的合法性
MEM *p = pm;
p = pm; while(p->next){ if(p->base + p->size next->base && p->next->base >= base
return true; + size){
} p= p ->next; } if(!p->next && p->base + p->size base + p->size = base + size)
} } return false;
bool release(MEM *pm, int base, int size){//释放内存空间
MEM *p = pm;
}
int allocMem(MEM *pm, int size){//分配内存空间
MEM *p = pm; int base; if(!check(pm, base ,size)){ } return false; while(p->next){ if(base + size next->base) break; p = p->next; } if(base == p->base + p->size){//低地址相邻 if(p ->next && p->next->base == base + size){//释放区上下都相邻 p->size += size + p->next->size; temp = p->next; p->next = p->next->next; free(temp); }else{//仅与地地址相邻 p->size += size; } }else if (p->next && p->next->base == base +size){//仅高地址相邻 p->next->base = base; p->next->size += size; }else{//释放区上下与空闲区都不相邻 } return true; temp = getMEM(); temp->size = size; temp->base = base; temp->next = p->next; p->next = temp;
} if(p->next->size >= size) break; p = p->next; if(!p->next) return -1;
if(p->next->size == size){//空闲分区大小刚好等于所需分区
base = p->next->base;
p->next = p->next->next;
}else{
p->next->size -= size;
base = p->next->base;
p->next->base += size;
}
return base;
}
void print(MEM *pm){//打印空闲分区表和空闲分区链
MEM *p = pm->next;
puts(
puts(
while(p){
printf(
p = p->next;
}
puts(
puts(
p = pm;
while(p->next){
printf(
}
if(p->base + p->size
printf(
}
void init(){//初始化总内存大小和空闲分区链的头结点
pm->base = 0;
pm->size = 0;
pm->next = getMEM();
puts(
}
SIZE = geti(); pm->next->size = SIZE; pm->next->base = 0; pm->next->next =NULL;
int main()
{
}
int size = 0,base = 0; int ch; init(); print(pm); while(1){ puts(
班_____ 姓名 张菲 协作者 无 教师评定_________________ 实验题目 文件系统
一、实验目的
模拟文件系统实现的基本功能,了解文件系统的基本结构和文件的各种管理方法,加深理解文件系统的内部功能及内部实现。通过用高级语言编写和调试一个简单的文件系统,模拟文件管理的工作过程,从而对各种文件操作命令的实质内容和执行过程有比较深入的了解。
二、实验内容和要求
编程模拟一个简单的文件系统,实现文件系统的管理和控制功能。要求本文件系统采用两级目录,即设置主文件目录[MFD]和用户文件目录[UED]。另外,为打开文件设置运行文件目录[AFD]。设计一个10个用户的文件系统,每次用户可保存10个文件,一次运行用户可以打开5个文件,并对文件必须设置保护措施。在用户程序中通过使用文件系统提供的Create、open、read、write、close、delete等文件命令,对文件进行操作
三、实验主要仪器设备和材料
硬件环境:IBM-PC或兼容机
软件环境:C语言编程环境
四、实验原理及设计方案
1、实验原理
运用二级目录思想来模拟文件系统。
为每个用户建立一个单独的用户文件目录UFD。这些文件目录具有相似的结构,它由用户文件的文件块组成。此外,在系统再建立一个主文件目录MFD;在主文件目录中,每个用户目录都占有一个目录项,其目录项中包含文件名和指向该文件目录文件的指针。
3、程序流程图
五、结果过程及截图
系统的使用简要说明:
登陆:
create命令新建文件
file5
Create失败
delete命令删除文件
Delete失败:
open打开命令,及write命令来追加文件
Read命令和close命令关闭
对没打开的文件读写:
六、所遇困难的解决以及心得体会
本实验的代码长度的最常的,但是原理很简单,并且实验要求的是模拟,所以大大降低了实验的难度,在操作系统课中我们还有一个课程设计,也是文件管理系统,但是要比这个难很多,通过这个实验,我对文件管理系统的运行机制有了深入的了解,因为要做好这个实验必须要懂得文件管理系统的架构,这也迫使我认认真真的研究了操作系统中关于文件管理这一章的内容。同时这个程序也为以后的课程设计奠定了基础,并且很多的代码也是可以重用的。
七、思考题
1、文件系统要解决哪些问题?
答:要解决文件和用户是否同名,文件创建数是否超额,所要求文件是否存在等问题。
2、什么是文件目录?什么是文件目录中包含哪些信息?目前广泛采用的目录结构形式是哪种?
答:文件目录就是用来展示一个用户的所有文件的一个表格,文件目录应包含文件名,保密码,文件大小,目前广泛采用的目录结构形式是多级目录形式。
八、源代码
#include
#include
#include
#include
#define NULL 0
typedef struct mdf{//MDF结构体
char username[20];//用户名 char filename[20];//文件名
struct mdf *next;
}MDF;
typedef struct ufd{//UFD结构体
char filename[20];//文件名
int protect;//文件保护码 unsigned int length;//文件长度
struct ufd *next;
}UFD;
typedef struct afd{//AFD结构体
char filename[20];//文件名 int protect;//文件保护码 unsigned int point;//文件读写指针
struct afd *next;
}AFD;
MDF *pmdf;//全局链表头指针
UFD *pufd;
AFD *pafd;
char UserUFD[20];//已经登陆成功的用户名
void initMDF()//初始化MDF表
{
FILE *fp; pmdf= (MDF*)malloc(sizeof(MDF)); MDF *p = pmdf; if((fp = fopen(
} exit(1); } while (!feof(fp)){//把MDF文件中的内容装入链表 } p->next = (MDF*)malloc(sizeof(MDF)); p = p->next; fscanf(fp,
void printUFD()//打印MDF表 {
UFD *p = pufd->next; puts(
}
void initUFD(char *name)//初始化UFD表 {
FILE *fp; pufd= (UFD*)malloc(sizeof(UFD)); UFD *p = pufd; if((fp = fopen(name,
} p->next = NULL; fclose(fp);
int checkuser()//检测登陆的用户名 {
} char username[20]; while(1){ puts(
void initAFD()//初始化AFD {
pafd = (AFD*)malloc(sizeof(AFD)); pafd->next = NULL; }
bool create()//创建文件命令 {
char filename[20]; UFD *p = pufd->next; AFD *pa = pafd; puts(
} } if(!p->next) break; p= p->next; p->next = (UFD*)malloc(sizeof(UFD)); p=p->next; strcpy(p->filename, filename); p->protect = 2; p->length = 0; p->next = NULL; while(pa->next){//创建文件后加入到AFD } pa=pa->next; pa->next = (AFD*)malloc(sizeof(AFD)); pa = pa->next; strcpy(pa->filename ,filename); pa->protect = 2; pa->point = 0; pa->next = NULL; return 1;
}
bool _delete()//删除文件命令 {
char filename[20]; puts(
puts(
return 0;
}
bool open()//打开文件命令
{
char filename[20]; unsigned int protect; puts(
} } puts(
void close()//关闭文件命令
{
char filename[20]; UFD *pu = pufd->next; puts(
int read()//读文件命令
{
char filename[20]; unsigned int length; AFD *p = pafd->next;
} puts(
int write()//写文件命令
{
}
char filename[20]; unsigned int length; AFD *p = pafd->next; puts(
void destroy()//释放内存
{
}
void saveUFD()//保存UFD文件
{
}
void bye()//推出系统
{ FILE *fp; UFD *p = pufd->next; if((fp = fopen(UserUFD,
UFD *pu = pufd->next; while(pa){ if(pa->protect == 2){ while(pu){ } saveUFD(); printUFD(); destroy(); } } if(strcmp(pa->filename, pu->filename) == 0){ } pu->length = pa->point; break; pu = pu->next; pa =pa->next;
}
void operate()//命令识别
{
while(1){ char command[20]; char name[][8] = {
}
} return; }else puts(
void print()
{
puts(
int main()
{
}
print(); initMDF(); checkuser(); initAFD(); operate();)//命令识别 return 0;