操作系统课程设计
实验讲义
黑龙江大学计算机科学技术学院 软件学院
2004年-2017年
目 录
第一部分 课程目的与任务
在学这门课之前,学生必须预修过高级语言、数据结构、离散数学方面的基本知识,并学习操作系统课程相关内容。
计算机科学与技术-计算机应用; 软件工程-软件工程; 电子信息科学类-电子信息科学与技术;管理类-信息管理专业
为配合“操作系统”课程的教学,通过模拟操作系统的实现,使学生能更深刻地领会操作系统工作原理和操作系统实现方法,并提高程序设计能力。
本课程要求实现一套模拟单用户多任务操作系统的软件程序。该软件通过5个互相关联的模块模拟进程管理、存储管理、设备管理和文件管理四部分的主要内容,最终形成一套完整的模拟程序。
第二部分 主要内容及基本要求
●基本要求:利用简单的结构和控制方法模拟进程结构、进程状态和进程控制。
●参考学时:4学时
●实验提示:
- 用PCB表示整个进程实体,利用随机数方法或键盘控制方法模拟进程执行中产生的事件,或者利用基于图形界面的鼠标或者键盘操作控制进程管理内容。
- 定义PCB:包括理论PCB中的基本内容,如内部ID、外部ID、队列指针。由于很难实现真正的进程创建功能,在实验中只需建立PCB节点,并用它代表一个完整的进程。每创建一个进程时,可动态分配PCB节点,对相应内容赋值,并链接到适当的队列上。
可参考如下数据结构(动态形式):
struct PCB{
char name[10];
struct PCB*next;
};
struct PCB *ready,*blocked,*running;
创建进程时:
struct PCB *p=(struct PCB *)malloc(sizeof(struct PCB));
并将此进程添加到就绪队列末尾:
add(ready,p);
其中,ready为就绪队列头节点,并在开始处分配了空间;add函数是链表中添加节点函数,代码可以为:
void add(struct PCB *head,struct PCB*process){
struct PCB*tmp=head;
while(tmp->next!=NULL)
tmp=tmp->next;
tmp->next=process;
process->next=NULL;
}
- 定义进程状态转换方式:真实的进程状态转换是由进程内部操作或操作系统的控制引起的。由于模拟程序中无法实现这些功能,我们可以采用随机数方法或键盘控制方法模拟,并实现对应的控制程序。随机方法指产生1-5的随机数,分别代表创建进程(1)、时间片到(2)、进程阻塞(3)、唤醒进程(4)、结束进程(5)等事件;键盘模拟方法指定义5个选项菜单代表以上五种事件。
- 根据随机数或键盘操作处理就绪队列、阻塞队列和当前执行进程的状态。每次事件处理后应显示出当前系统中的执行进程是哪一个,就绪队列和阻塞队列分别包含哪些进程。
- *(带*部分为试验班和特长班需要完成的内容,普通班选作)完成可变分区的分配与回收,创建进程的同时申请一块连续的内存空间,结束进程同时回收分配的内存空间。分配采用最佳适应算法,碎片大小为2Kb,最后回收所有进程的空间。可以查看进程所占的空间和系统空闲空间。
●评分标准:满分15分
要求必须完成以下几个方面的内容:
- 能够模拟进程的创建与撤销过程;5分
- 可对进程的状态进行全面的控制;5分
- 按先进先出方式管理就绪和阻塞队列,能够按队列形式输出进程状态。5分
●基本要求:在第1部分实验基础上实现进程的分页式内存分配和地址转换过程,并进一步实现请求分页式存储分配和地址转换过程。页面置换算法至少应实现先进先出(FIFO)、最近最久未使用(LRU)等算法。
●参考学时:6学时
●实验提示:
- 建立1个位示图,用来模拟内存的分配情况,位示图的位数与设定的物理块个数相同。程序启动时可利用一组随机0和1填充位示图,表示内存已被占用情况。
假设内存容量为64K,块大小为1K,则共有64个块,随机填充后的位示图可能的值如下:
第0字节 |
1 0 1 1 0 0 1 1 |
第2字节 |
1 1 1 0 0 1 1 1 |
第4字节 |
0 0 0 1 1 1 1 1 |
第6字节 |
1 1 1 1 1 0 0 0 |
第1字节 |
0 1 1 0 1 1 0 0 |
第3字节 |
1 0 0 0 0 1 1 1 |
第5字节 |
0 1 1 1 0 0 1 1 |
第7字节 |
0 0 0 0 0 0 0 1 |
该位示图表示内存的2(0字节第2位)、3(0字节第3位)、6(0字节第6位)、8(1字节第0位)、9(1字节第1位)、12(1字节第4位)、15(1字节第7位)…等块没有被占用。
- 创建进程时输入进程大小,并根据程序中设定的物理块大小为进程分配物理块,同时建立页表。例如,在上图基础上,若要建立一个大小为5000字节的进程,则:
- 计算出该进程共有“向上取整(5000/1024)=5”个页,需要占用5个内存块;
- 建立空的页表,即长度为5的一维整数数组;
- 从位示图中找出前5个“0”位在整个位示图中的位置号(即i字节j位为0,则该位的位置为8*i+j),并将这些号依次填入页表中,同时把前5个“0”改为“1”,以示对应内存块已经分配。
- 输入当前执行进程所要访问的逻辑地址,并将其转换成相应的物理地址。
- 进程退出时,根据其页表内容向位示图反向回填“0”。
- 扩充页表,将其变成支持请求和置换功能的二维页表(增加存在位等)。创建进程时可装入固定的前三页(或键盘输入初始装入页数,不同进程的装入个数可以不同),其余页装入到置换空间内(置换空间大小应为内存空间大小的1.5-2倍,对其还需建立独立的置换空间位示图)。
- 分别采用FIFO或LRU置换算法对地址转换过程中遇到的缺页现象进行页面置换,可将多次地址转换过程中所涉及到的页号视为进程的页面访问序列,从而计算置换次数和缺页率。
- *可以在完成第六步的基础上实现OPT的页面置换算法,并加以比较。
●开发流程:
基础部分:
1、在进程管理的基础上扩充主函数,添加新菜单:显示位示图dispaly_bitmap、显示进程页表display_pagetable、地址转换address_tranlate
2、定义页表节点结构体,扩充PCB结构(进程大小、页表),添加内存位示图
3、编写通用函数、位操作(读取某位,设置某位)
4、编写显示位示图和页表的函数,修改创建进程函数和终止进程函数
5、编写地址变换函数
struct PCB{
char name[10];
int size;
int *page_table;
struct PCB*next;
};
char bitmap[8];
通用函数:
位示图初始化
设置位1或0
void setbit(char *b,int bit_no,int flag)
{
char mask=(char)1<<bit_no;
if(flag)
*b=*b|mask;
else
{
mask=~mask;
*b=*b&mask;
}
}
int getbit(char b,int bit_no) 获得该位的数字
{
char mask=(char)1<<bit_no; 只有一位为一
if(b&mask)
return 1;
else
return 0;
}
int get_freeblock() 分配内存块,查找0并置成1,返回内存块号
{
int i,j;
for(i=0;i<MEM_SIZE/8;i++)
for(j=0;j<8;j++)
{
if(!getbit(bit_map[i],j))
{
setbit(&bit_map[i],j,1);
return i*8+j;
}
}
printf("内存已满\n");
return -1;
}
显示位示图:
display_bitmap();
修改创建进程函数:
创建进程时提示输入进程大小
根据进程大小申请页表page_table
tmp->page_table=(struct Page_Table *)malloc(sizeof(struct Page_Table)*(int)ceil(tmp->size/(double)BLOCK_SIZE));
for(i=0;i<(int)ceil(tmp->size/(double)BLOCK_SIZE);i++)
{
*(tmp->page_table+i)=get_freeblock();
}
地址变换address_translate ()
printf("请输入逻辑地址\n");
scanf("%d",&addr);
int pageno=(int)(addr/BLOCK_SIZE);
int offset=addr%BLOCK_SIZE;
int blockno= *(running->page_table+pageno);
printf("该逻辑地址的物理地址为:%d\n",blockno*BLOCK_SIZE+offset);
修改终止进程函数:Destroy()
遍历当前运行进程的页表:Display_pagetable();
高级部分:
1、在第一部分基础上,添加新菜单项(页面置换算法)
2、扩展页表结构、扩展PCB结构(页面访问队列)、添加对换空间位示
图
3、编写通用函数:页面访问队列操作(页面是否存在,删除页面,添加页面)
4、修改显示位示图和显示页表的函数,修改创建进程,终止进程函数,修改地址变换函数
5、编写页面置换算法函数
struct Page_Table{
int pageno,blockno,swapspaceno; //页号,块号,置换块号
int exists,modified; //存在位,修改位
};
struct Access_Queue{ //页面访问队列
int pageno;
struct Access_Queue *next;
};
struct PCB{
char name[20];
int size;
struct Page_Table *page_table; //存储块号
struct Access_Queue *fifo,*lru;
struct PCB *next;
};
char bit_map[MEM_SIZE/8];
char swap_bit_map[SWAP_SPACE_SIZE/8];
int getFreeSwapBlock()
修改创建进程函数:
void create()
{
.....
构建页表
if(i<LODE_COUNT) // LODE_COUNT是限定的该进程占用的内存块数
{
(tmp->page_table+i)->pageno=i;
(tmp->page_table+i)->blockno=get_freeblock();
(tmp->page_table+i)->swapspaceno=-1;
(tmp->page_table+i)->exists=1;
(tmp->page_table+i)->modified=0;
}
else
{
(tmp->page_table+i)->pageno=i;
(tmp->page_table+i)->blockno=-1;
(tmp->page_table+i)->swapspaceno=getFreeSwapBlock();
(tmp->page_table+i)->exists=0;
(tmp->page_table+i)->modified=0;
}
tmp->fifo=(struct Access_Queue *)malloc(sizeof(struct
Access_Queue));
tmp->fifo->next=NULL;
tmp->lru=(struct Access_Queue *)malloc(sizeof(struct
Access_Queue));
tmp->lru->next=NULL;
for(i=0;i<LODE_COUNT;i++)
{
if(i<(int)ceil(tmp->size/(double)BLOCK_SIZE))
{
addAccessQueue(tmp->fifo,i);
addAccessQueue(tmp->lru,i);
}
}
。。。。。
}
修改地址变换函数
void address_translate() //地址变换
{
。。。。。
if((running->page_table+pageno)->exists==1)
printf("该逻辑地址的物理地址为:%d\n",(running->page_table+pageno)->blockno*BLOCK_SIZE+offset);
else
{
printf("页号%d不在内存\n",pageno);
if(strcmp(swap_algorithm,"FIFO")==0)
}
●评分标准(满分15分):
要求必须完成以下几个方面的内容(实验提示前4部分,10-12分):
- 在第1部分基础上扩展;
- 能够模拟内存的分页式分配和回收过程,可查看内存分配位示图和进程页表;
- 可根据内存分配状态进行地址转换。
如果实现以下要求(实验提示后3部分),有额外3-5分加分:
- 能够模拟基于虚拟存储器的内存分配和回收过程,可查看交换空间位示图和扩展的页表;
- 在虚拟存储器基础上完成地址转换,缺页时能够实现页面置换;
- 页面置换过程中能够模拟FIFO、LRU置换算法,可将多次地址转换过程中所涉及到的页面视为进程的页面访问序列,从而计算置换次数和缺页率。
●基本要求:在前面的实验基础上实现设备管理功能的模拟,主要包括通道和控制器的添加和删除,设备的添加、删除,设备的分配和回收。
●参考学时:6学时
●实验提示:
- 假定模拟系统中已有键盘、鼠标、打印机和显示器四个设备,另有三个控制器和两个通道。
- 设备管理子系统涉及到系统设备表(SDT)、通道控制表(CHCT)、控制器控制表(COCT)和设备控制表(DCT)来体现输入输出系统的四级结构和三级控制。应实现上述数据结构来完成对外围设备的管理。
数据结构参考代码:
C/C++版:
struct Node{
char name[10]; //名称
struct PCB *process; //占用该节点的进程
struct PCB *waitinglist; //等待该节点的进程队列
struct Node *next; //下一个节点
struct Node *parent; //父节点
};
struct Node *CHCTs; //通道队列头节点
struct Node *COCTs; //控制器队列头节点
struct Node *DCTs; //设备队列头节点
Java版:
class IONode{
String name;
IONode next;
Process process;
Vector<Process> waitinglist;
IONode parent;
}
class CHCT extends IONode{}
class COCT extends IONode{}
class DCT extends IONode{}
- 实现上述设备、控制器以及通道的层次关系,同时能够添加或删除新的设备、控制器或通道。
上述数据结构之间的关系如下图所示:
CHCT链表 |
COCT链表 |
DCT链表 |
通道1 |
通道2 |
控制器1 |
设备1 |
控制器2 |
设备2 |
控制器3 |
设备3 |
设备4 |
设备5 |
next指针 |
next指针 |
next指针 |
parent指针 |
parent指针 |
占用的进程 |
等待的进程 |
等待的进程 |
process指针 |
waitinglist链表 |
占用的进程 |
等待的进程 |
等待的进程 |
process指针 |
waitinglist链表 |
占用的进程 |
等待的进程 |
等待的进程 |
process指针 |
waitinglist链表 |
- 通过键盘命令模拟进程执行过程中提出的设备分配或释放请求,并为此请求分配或释放设备。分配设备成功后可将进程状态调整为阻塞,释放设备后变为就绪状态。
- 分配设备时应如果该设备已被其它进程占用,则设备分配失败,请求进程进入阻塞状态,同时等待该设备的释放。如果设备空闲,进程占用设备的同时还应提出申请控制器请求,直到与设备相关的通道都已申请成功为止。
- 设备、控制器或通道的释放应引起对应节点的等待队列中的第一个阻塞进程被唤醒。如果被唤醒的进程还未完成申请操作,应继续执行上级节点的申请操作。
- 设备分配流程:
process==null(设备空闲)? |
执行进程提出设备分配请求 |
在设备链表DCTs中找到所申请的设备 |
process=执行进程,向上找到控制器 |
process==null(控制器空闲)? |
process=执行进程,向上找到通道 |
process==null(通道空闲)? |
process=执行进程,执行进程变阻塞状态, 就绪进程变为执行状态 |
分配成功或失败,返回 |
执行进程进入该设备的waitinglist队列并变为阻塞状态,就需进程变为执行状态 |
执行进程进入该控制器的waitinglist队列并变为阻塞状态,就需进程变为执行状态 |
执行进程进入该通道的waitinglist队列并变为阻塞状态,就需进程变为执行状态 |
Y |
Y |
Y |
N |
N |
N |
- 回收流程:
d->process==p? |
进程p提出释放设备d的请求 |
在DCTs中找到所释放的设备d |
为第1个等待进程分配设备、控制器… |
回收结束,返回 |
Y |
N |
有等待d的进程(waitinglist不空)? |
N |
ch->process==p? |
Y |
N |
有等待进程(waitinglist不空)? |
N |
找到控制器的通道ch(即co->parent) |
ch->process=第1个等待进程 |
co->process==p? |
Y |
N |
有等待进程(waitinglist不空)? |
N |
找到设备的控制器co(即d->parent) |
为第1个等待进程分配控制器、通道 |
出错返回 |
Y |
Y |
d->process=null |
co->process=null |
ch->process=null |
9、*体现设备独立性,可以在设备分配时只指定设备类型,如果有空闲设备即可分配。
●评分标准(满分15分):
要求必须完成以下几个方面的内容(10-12分):
- 在第1、2部分基础上扩展;
- 能够模拟设备的分配与回收流程;
- 设备分配成功与否,进程都应进入阻塞状态;
- 能够较形象地输出通道、控制器、设备的层次关系以及进程的占用、等待状态;
也可实现如下扩充要求(3-5分):
- 能够添加、删除通道、控制器或设备;
- 能够模拟SDT基础上的设备分配过程。
●基本要求:利用磁盘文件实现操作系统的文件管理功能,主要包括目录结构的管理、外存空间的分配与释放以及空闲空间管理三部分。
●参考学时:6学时
●实验提示:
- 通过初始化操作建立一个模拟外存空间的虚拟磁盘文件,在该文件中保存目录和文件内容。创建该文件时应创建初始的根目录内容、文件分配表。根目录实为一特殊文件,其开始内容为空,大小为一个块。
- 文件目录项(可以采用FCB格式)应包括类型(目录 or文件)、创建日期、大小、第一个磁盘块块号。
- 显示命令提示符“$”,并根据输入命令完成相应的文件操作:
- MD(创建子目录):创建目录文件,并在父目录文件中增加目录项。
- CD(切换工作目录):根据当前目录切换到指定目录。
- RD(删除子目录):搜索所要删除的目录是否为空目录,若是则删除。
- MK(创建空文件):创建指定大小的文件(如输入命令 “mk test 2000”,表示创建大小为2000字节的test文件),并在父目录中添加文件名称;还应对FAT表进行适当修改。
- DEL(删除文件):如果所要删除的文件存在,则删除,同时修改父目录内容;还应对FAT表进行适当修改。
- DIR:列出当前目录的所有目录项。
- FORMAT:根据进一步的虚拟磁盘文件名和块个数信息创建出虚拟磁盘文件。
- *TREE:根据磁盘文件和目录按着树形结构加以显示。
- 实现提要
- 首先应确定采用FAT16表,块大小固定为1024Byte;
- 定义目录项结构:
struct FCB{ //文件或目录控制块
char name[8]; //文件名最长不超过8个字符,这样整个FCB大小正好为32Byte
int size; //大小
int first_block; //第一块块号
char type; //类型,1为文件,2为目录,0为已删除目录项
char datetime[15]; //日期时间,格式为yyyymmdd hhmmss
};
-
- 定义两个宏:EMPTY_BLOCK和LAST_BLOCK,用于表示FAT表中的特殊内容
#define EMPTY_BLOCK 0x0000
#define LAST_BLOCK 0xFFFF
-
- 格式化时创建虚拟磁盘文件。例如,创建块大小为1024Byte,块个数为8个的虚拟磁盘文件时,所建立的文件内容为
0000 | 0000 | 0000 | 0000 | 0000 | 0000 | 0000 | 0000 | FAT16表,共占用16个字节 |
| [0]块 | |||||||
| [1]块 | |||||||
| [2]块 | |||||||
| [3]块 | |||||||
| [4]块 | |||||||
| [5]块 | |||||||
| [6]块 | |||||||
| [7]块 |
-
- 在上面的虚拟磁盘文件中自动创建大小为1块的根目录文件后变成:
FFFF | 0000 | 0000 | 0000 | 0000 | 0000 | 0000 | 0000 | FAT16表,共占用16个字节 |
空的根目录文件,占用[0]号块,FAT表有相应变化 | [0]块 | |||||||
| [1]块 | |||||||
| [2]块 | |||||||
| [3]块 | |||||||
| [4]块 | |||||||
| [5]块 | |||||||
| [6]块 | |||||||
| [7]块 |
-
- 继续创建test子目录后变成:
FFFF | FFFF | 0000 | 0000 | 0000 | 0000 | 0000 | 0000 | FAT16表,共占用16个字节 |
test 1 1024 2012-11-20 20:16:72 1 | [0000]块 | |||||||
空的test目录文件,占用[1]号块,FAT表也有相应变化 | [0001]块 | |||||||
| [0002]块 | |||||||
| [0003]块 | |||||||
| [0004]块 | |||||||
| [0005]块 | |||||||
| [0006]块 | |||||||
| [0007]块 | |||||||
| Ffff |
-
- 创建大小为2000Byte的文件a.c后变成:
FFFF | FFFF | 3 | 4 | Ffff | 0000 | 0000 | 0000 | FAT16表,共占用16个字节 |
test 1 1024 2012-11-20 20:16:72 1 a.c 0 2000 2012-11-20 20:18:25 2 | [0]块 | |||||||
空的test目录文件,占用[1]号块,FAT表也有相应变化 | [1]块 | |||||||
文件a.c的前1024字节 | [2]块 | |||||||
文件a.c的后1024字节 | [3]块 | |||||||
文件a.c的后122字节 | [4]块 | |||||||
| [5]块 | |||||||
| [6]块 | |||||||
| [7]块 |
-
- 删除空目录test后:
FFFF | 0000 | 3 | FFFF | 0000 | 0000 | 0000 | 0000 | FAT16表,共占用16个字节 |
a.c 0 2000 2012-11-20 20:18:25 2 | [0]块 | |||||||
空块 | [1]块 | |||||||
文件a.c的前1024字节 | [2]块 | |||||||
文件a.c的后976字节 | [3]块 | |||||||
| [4]块 | |||||||
| [5]块 | |||||||
| [6]块 | |||||||
| [7]块 |
●评分标准(满分15分):
要求必须完成以下几个方面的内容(10-12分):
- 能够在磁盘文件基础上模拟外存分配与回收流程;
- 支持dir、md、cd、rd命令。
也可实现如下扩充要求(3-5分):
- 可较方便查看位示图、索引节点、目录树关系
- 能够实现创建文件的mknod命令以及删除文件的del命令,可对所创建的文件内容进行简单的编辑。
●基本要求:在1、2、3阶段实验基础上实现按先来先服务FCFS、短作业优先SJF以及时间片轮转算法调度进程的模拟过程。
●参考学时:6学时
●实验提示:
- 程序开始运行时选择调度算法,创建进程时输入进程所需服务时间以及到达时间。可参考如下数据结构:
struct PCB{
char name[10];
int size;
int arrival_time; //到达时间
int burst_time; //服务时间
int finished_time; //结束运行时间
int runned_time; //已运行时间
struct PCB*next;
};
struct PCB *ready,*blocked,*running,*finished;
其中,finished队列是已运行结束的进程队列,便于统计各项时间数据。
- 根据当前所设定调度算法,连续调度所有进程,并计算每个进程的周转时间和带权周转时间、所有进程的平均周转时间和平均带权周转时间。
- 调度时应适当输出调度过程中各进程状态队列的变化情况以及进程的已执行时间、还需服务时间(针对时间片轮转算法)。
- *完成银行家算法的实现。
●评分标准(满分15分):
要求必须完成以下几个方面的内容(10-12分):
- 在第1-3部分基础上扩展;
- 支持FCFS、短作业优先以及时间片调度算法。
也可实现如下扩充要求(3-5分):
- 能够较方便地查看调度过程及平均周转时间、平均带权周转时间;
- *支持优先权调度算法与其它算法相结合的调度模式。
“操作系统课程设计”总结报告
学号 |
|
姓名 |
|
学院 |
|
年级 |
|
专业 |
|
报告日期 |
|
成绩 |
|
黑龙江大学计算机科学技术学院 软件学院
本学期开设了操作系统课程,为了更好的理解操作系统理论,掌握其应用,特设此操作系统实验课程,在该操作系统实验中包括进程管理、存储管理、设备管理和文件管理等五个部分。
1.1目的
1.2内容
1.3数据结构
1.4算法设计及流程图
二、请求分页存储器管理 2.1目的
2.2内容
2.3数据结构
2.4算法设计及流程图
2.5小结
三、设备管理 3.1目的
3.2内容
3.3数据结构
3.4算法设计及流程图
3.5小结
四、文件管理 4.1目的
4.2内容
4.3数据结构
4.4算法设计及流程图
4.5小结
五、进程调度 5.1目的
5.2内容
5.3数据结构
5.4算法设计及流程图
5.5小结
六、课程总结
|