资源描述
操作系统课程设计
目 录
第一章 课程设计目的和要求 1
1.1 课程设计目的 1
1.2 课程设计要求 1
第二章 课程设计任务内容 2
2.1课程设计任务 2
2.2课程设计内容 2
第三章 详细设计说明 3
3.1问题描述 3
3.2模块设计 3
3.3程序流程图 4
3.4算法与数据结构 5
3.4.1数据结构 5
3.4.2算法描述 5
3.4.3算法流程图 6
第四章 软件使用说明 10
4.1系统开发与运行环境 10
4.2系统的运行说明 10
4.3 运行结果 10
第五章 课程设计心得体会 15
附录1:参考文献 16
附录2:程序清单 17
1
第一章 课程设计目的和要求
1.1 课程设计目的
操作系统是计算机系统的核心和灵魂,是计算机系统必不可少的组成部分,也是计算机专业教学的重要内容。该课程概念众多、内容抽象、灵活性与综合性强,不但需要理解操作系统的概念和原理,还需要加强操作系统实验,上机进行编程实践,故进行此次课程设计,使我们更好地掌握操作系统的精髓,真正做到深刻理解和融会贯通。
操作系统课程设计是本课程重要的实践教学环节。课程设计的目的,一方面使学生更透彻地理解操作系统的基本概念和原理,使之由抽象到具体;另一方面,通过课程设计加强学生的实验手段与实践技能,培养学生独立分析问题、解决问题、应用知识的能力和创新精神。此次课程设计给学生更多自行设计、自主实验的机会,充分放手让学生真正培养学生的实践动手能力,全面提高学生的综合素质。
本次课程设计的题目为读者写者同步问题的实现,在熟练掌握进程同步原理的基础上,利用C程序设计语言在windows操作系统下模拟实现读者写者同步问题的功能,一方面加深对原理的理解,另一方面提高根据已有原理通过编程解决实际问题的能力,为进行系统软件开发和针对实际问题提出高效的软件解决方案打下基础。
1.2 课程设计要求
在深入理解操作系统基本原理的基础上,对于选定的题目以小组为单位,先确定设计方案,设计系统的数据结构和程序结构,设计每个模块的处理流程。要求设计合理,编程序实现系统,要求实现可视化的运行界面,界面应清楚地反映出系统的运行结果,确定测试方案,对系统进程测试,运行系统并要通过验收,讲解运行结果,说明系统的特色和创新之处,并提交课程设计报告。
第二章 课程设计任务内容
2.1课程设计任务
要求完成的主要任务: (包括课程设计工作量及其技术要求,以及说明书撰写等具体要求)
1.技术要求:
1)为每个读者/写者产生一个线程,设计正确的同步算法;
2)读者应有3个以上,写者应有有两个以上;
3) 必须包含读者优先唤醒和写者优先唤醒两种算法。
2.设计说明书内容要求:
1)设计题目与要求;
2)总的设计思想及系统平台、语言、工具等;
3)数据结构与模块说明(功能与流程图);
4)运行结果与运行情况。
2.2课程设计内容
本次课程设计是实现读者写者的同步与互斥问题,要求实现读者与读者可以共享资源,读者与写者互斥,写者与写者互斥,要求编写出完整的代码,并截取运行结果截图。
第三章 详细设计说明
3.1问题描述
所谓读者写着问题,是指保证一个writer进程必须与其他进程互斥地访问共享对象的同步问题。
读者写者问题可以这样的描述,有一群写者和一群读者,写者在写同一本书,读者也在读这本书,多个读者可以同时读这本书,但是,只能有一个写者在写书。为实现读写同步,需要使用信号量机制。
信号量机制是支持多道程序的并发操作系统设计中解决资源共享时进程间的同步与互斥的重要机制,而读者写者则是这一机制的一个经典范例。
对利用信号量来解决读者—写者问题的描述如下:
1)写-写互斥,即不能有两个写者同时进行写操作;
2)读-写互斥,即不能同时有一个读者在读,同时却有一个写者在写;
3)读读允许,即可以有2个以上的读者同时读。
本次课程设计中,在处理读者写者等待队列的请求时,用到了两种唤醒进程算法:读者优先唤醒算法和写者优先唤醒算法。
读优先:当一个读者与若干写者同时处于等待队列中时,并且此时并无写操作进行时,读者优先进行读。
写优先:当一个读者与若干写者同时处于等待队列中时,并且此时并无写操作进行时,写者优先进行写。
3.2模块设计
1. 程序由三部分组成:
1) 主程序:显示主菜单,调用各个子菜单;
2) 读者优先唤醒:写者释放资源时,优先唤醒等待队列中的读进程;
3) 写者优先唤醒:写者释放资源时,优先唤醒等待队列中的写进程。
主程序
读者优先唤醒
写者优先唤醒
退 出
图3-1模块划分图
3.3程序流程图
结 束
读者优先唤醒
开始
显示主菜单
输入choice
Choice=1?
Choice=2?
Choice=3?
写者优先唤醒
退出
N
N
N
Y
Y
Y
图3-2为本次课程设计的程序流程图,其中choice为一整型变量,由使用者通过键盘输入:
图3-2程序流程图
3.4算法与数据结构
3.4.1数据结构
1. 定义一个结构体,用来存放读者等待队列,其元素为用来存放读进程的数组reader[200],用来指示等待队列中读进程个数的整型变量index:
struct rqueue
{
int readers[200];
int index;
}rq;
2. 定义另一个结构体,用来存放写者等待队列,其元素为用来存放写进程的数组writers[200];用来指示等待队列中写进程个数的整型变量index:
struct wqueue
{
int writers[200];
int index;
}wq;
3.4.2算法描述
1. 定义一个数据缓存buffer及用于实现同步互斥的信号量w。
2. 定义一个读者函数:
1) 当有写者在占用buffer时,读者应该等待,直到写者不再使用该buffer;
2) 当有其他读者在占用buffer时,读者可对buffer进行读取操作;
3) 当buffer中有数据时,则从其中读取一个数据,并显示然后退出;
4) 当buffer中没有数据时,应等待,直到buffer中有数据可读,用变量sign指示,为1时才能读;为0时需要等待写者写数据。
3. 定义一个写者函数:
1) 当有读者在占用buffer时,写者应该等待,直到所有的读者都退出为止;
2) 当有其他写者占用buffer时,该写者应该等待,直到占用buffer的写者退出为止;
3) 当buffer有空闲时,写者应该在buffer中写入一个数据并退出。
4. 定义主函数,在其中可以任意创建读者与写者。
可根据用户输入创建读者或写者进程(线程)。
3.4.3 算法流程图
1) 读操作算法流程图如图3-3所示:
Y
开 始
Rcount=0?
w=1?
sign=1?
W=0;rcount++;
进行读
等待写者写内容;
并加入到读者唤醒队列;
Rq.index++;
结束
Y
Y
N
N
N
图3-3读者算法流程图
注:Rcount表示正在进行读操作的数目;w表示一个信号量,为1时表示无进程操作,缓冲区空闲,为0时表示有进程占用;sign表示缓冲区是否有数写入,只有缓冲区中有数才能进行读操作,为1表示有数,为0表示没有;Rq.index表示读操作等待唤醒队列进程数;
2) 写操作算法流程图如图3-4所示:
开 始
W=1?
W--;进行写;
加入写唤醒队列
Wq.index++;
结束
N
Y
图3-4写者算法流程图
注:w含义同上;Wq.index表示写操作等待唤醒队列进程数。
3) 读者优先唤醒算法流程图如图3-5所示:
读者优先唤醒是指当写者释放其占有的资源时,当读者和写者同时有进程等待,优先处理读进程。
图3-5注:Rcount.w.Wq.index含义同上;reader_wait是布尔型变量,为真时表示有读者等待,反之没有读者进程等待。
4) 写者优先唤醒算法流程图如图3-6所示:
写者优先唤醒是指当写进程释放其占有资源时,当读者和写者同时有进程等待,优先处理写进程。
图3-6注:Rcount,w,Wq.index含义同上;write_wait是一个布尔型变量,为真时表示有写进程等待,反之没有。
N
N
Y
N
N
Y
Y
Y
Y
Wq.index=0?
处理队列首部的写进程
N
!reader_wait?
开始
Rcount=0?
reader_wait=false;w=1;
结束当前写进程
Rq.index=0?
reader_wait=true;
W=0;处理读进程
Wq.index!=0?
Rcount=0;
W=1;
处理写进程
W=0;
结束
图3-5读者优先唤醒算法流程图
Y
N
N
Y
Y
Y
N
N
Y
开始
Rcount=0?
write_wait=false;w=1;
结束当前写进程
Wq.index=0?
write_wait=true;
W=0;依次处理写进程
Wq.index!=0?
Rcount=0;
W=1;
处理写进程
W=0;
!write_wait?
Rq.index=0?
处理读进程; w=0
结束
N
图3-6 写者优先唤醒算法流程图
第四章 软件使用说明
4.1系统开发与运行环境
代码实现:C语言程序
开发工具:Mcrosoft Visual C ++ 6.0
运行环境:windows 7
4.2系统的运行说明
对读者写者问题系统的运行说明如下:按照显示菜单说明,输入数字对菜单进行选择,一个菜单即是一个功能实现,可以实现读者写者进程同步与互斥问题。
4.3 运行结果
1. 系统主菜单及读者优先唤醒子菜单,按菜单提示选择1是读者优先唤醒算法,运行结果如图4-1所示:
图4-1主菜单及子菜单
2. 在子菜单中选择1创建读进程,由于此之前并无写者向缓冲区中写数,所以缓冲区为空,此时不可读,将读者加入到等待唤醒队列,读者队列数目加1,运行结果如图4-2所示:
图4-2 创建读进程
3. 在子菜单中选择2创建写进程1,由于并无进程占用缓冲区,所以写者1不必等待可以直接写数,运行结果如图4-3所示:
图4-3 创建写进程
4. 在子菜单中选择1创建读者进程,由于已有写进程占用所以将读者进程加入到读者等待唤醒队列;选择创建写者进程2,由于写者与写者互斥,将写者2加入到写者等待唤醒队列,运行结果如图4--4所示:
图 4-4创建读进程和写进程
5. 选择3写者1释放资源,由于此为读者优先唤醒算法,所以优先唤醒读者,进行读进程操作,运行结果如图4-5所示:
图 4-5写者释放占有资源优先唤醒读者
6. 选择1创建读进程,读者与读者之间不互斥,所以不必等待;选择2创建写进程3,由于读进程占用资源,所以写者必须等待,将写者加入等待队列,运行结果如图4-6所示:
图 4-6 创建读进程和写进程
7. 选择3读者释放资源,写者2被唤醒进行写操作,运行结果如图4-7所示:
图 4-7 读者释放占有资源
8. 返回到主菜单并选择写者优先唤醒算法,运行结果如图4-8所示:
图 4-8 写者优先唤醒算法
9. 选择3释放当前进程,等待队列中的写者唤醒,运行结果如图4-9所示:
图 4-9 释放写者2,写者3占有资源
10. 创建两个读进程和一个写者进程,由于写者3占有资源,所以它们都要加入各自等待唤醒队列中,运行结果如图4-10所示:
图4-10 创建读进程和写进程
11. 释放当前写者占有资源,由于本算法使用的是写者优先唤醒算法,所以写者4优先被唤醒占用资源,进行写操作,读者进程依然等待,运行结果如图4-11所示:
图4-11写者释放资源优先唤醒等待队列中的写者
第五章 课程设计心得体会
三周的课程设计结束了,这次课程设计是操作系统中读者与写者问题。通过课程设计我懂得了读者与写者的一些简单算法的实现,掌握了用高级语言编写和调试程序的方法,加深了对读者写者问题概念及算法的理解,并且加深了对进程同步算法的理解。
同时,在这次课程设计中,也暴露出一些问题,比如动手之前的构思是非常的完美,本来以为会一气呵成的顺利完成任务,可是正是需要动手做得时候却是那么困难,尤其是编写代码的时候,编完运行老是出错,然后改正,还是出错,总是在那些细节上出错,问题虽然不大,但就是不能运行。所以,平时还是要多动手,注意要认真细心。
最后,感谢老师对我的学习的指导!
附录1:参考文献
[1] 胡志刚,谭长庚等,《计算机操作系统》,中南大学出版社2005年
[2] 罗宇,邹鹏等,《操作系统》(第二版),电子工业出版社2007年4月
[3] 汤子瀛,哲风屏,汤小冉等,《计算机操作系统》,西安电子科技大学出社,2001年8月
[4] 张尧学,史美林,《计算机操作系统课程》,清华大学出版社,2000年
[5] 庞丽萍,《操作系统原理》,华中理工大学出版社,2000年
[6] 马季兰等Linux操作系统,电子工业出版社2002年
[7] 任爱华,李鹏,刘方毅,操作系统实验指导,清华大学出版社,2004年
[8] 谭浩强著.C程序设计,清华大学出版社,1999年12月第2版
[9] 谭浩强著.C++程序设计实践指导,清华大学出版社,2005年7月底1版
[10]【美】D.C.Malik:C++编程——从问题分析到程序设计,电子工业出版社,2003年7月第一版
附录2:程序清单
源代码:
#include<stdio.h>
#include<stdlib.h>
int rcount=0; //正在读的读者数量
int wcount=0; //写者队列中等待写操作的写者数量
int read_id=0; //读进程号
int write_id=0; //写进程号
int w=1; //读写互斥信号量;
int choice; //用户选择读者优先或者写者优先
int sign=0; //标识缓冲区是否为空,0表示空,1表示不为空
void WFwakeup();
void RFwakeup();
struct rqueue{ //读者等待队列
int readers[200];
int index;
}rq;
struct wqueue{ //写者等待队列
int writers[200];
int index;
}wq;
/*void first(){ //初始化
int i;
rq.index = 0;
wq.index = 0;
for(i = 0;i<20;i++){
rq.readers[i] = 0;
wq.writers[i] = 0;
}
}*/
//*******************************************读进程读操作
void read()
{
int i = 0;
read_id++;
if(rcount == 0){ //当前没有读进程在读:可能有写进程在写,也可能CPU空闲
if(w==1)
{ //如果CPU空闲,读者拿到获得cpu
w--; //相当于一个P操作
rcount++;
if(sign ==0)
{
if(choice == 1){
rq.readers[rq.index++]=read_id; //将读者进程加入等待队列
RFwakeup();
return;
}
else{
rq.readers[rq.index++]=read_id; //将读者进程加入等待队列
WFwakeup();
return;
}
}//if
printf("读者%d正在读\n",read_id);
}//if
else{ //写者线程正在执行
printf("!有写者在写不能读!\n");
rq.readers[rq.index++]=read_id; //将读者进程加入等待队列
}//else
}//if
else{ //rcount !=1 则知道当前已经有读者在读,读读不互斥,则这个读者可以直接进来了读
printf("读者%d正在读\n",read_id);
}//else
}
//***************************写进程写操作
void write(){
write_id++;
if(w == 0){
if(rcount != 0 ){ //有读者进程在执行
printf("!有读者在读不能写!\n");
wq.writers[wq.index++]=write_id; //将写者进程加入等待队列
wcount++;
return;
}
if(rcount == 0 ){ //rcount == 0则当前无读者,但w = 0,所以有写者在写
printf("!有写者在写不能写!\n");
wq.writers[wq.index++]=write_id; //将写者进程加入等待队列
wcount++;
return;
}
}
if(w == 1){
w--;
printf("写者%d正在写\n",write_id);
sign=1;
}//if
}
//************************读者优先时唤醒进程
void RFwakeup(){
int i = 0;
int j = 0;
int m;
m = rq.index;
// n = wq.index;
if(rcount == 0){ //当前无读进程,是写者在写停止运行写进程
bool reader_wait=false;
w=1;
printf("写者已经写完\n");
sign = 1; //缓冲区中已经有内容要置1
for(i=0;i<=m;i++){ //index为当前读者队列中的等待进程数
if(rq.readers[i]!=0){
reader_wait=true; //确实有读者在等待
printf("等待的读者%d正在读\n",rq.readers[i]);
w = 0;
rq.readers[i]=0;
rcount++;
rq.index--;
}//if
}//for
if(!reader_wait){ //没有读者等待,看是否有写者等待
for(int i=0;i<=wq.index;i++){ //检查写者等待队列
if(wq.writers[i]!=0){
w = 0;
printf("等待的写者%d正在写\n",wq.writers[i]);
wq.writers[i]=0;
wcount--;
break;
}//if
}//for
}//if
}//if
else{ //若rcount != 0则读者正在读,停止读者进程,此时若有等待必为写者
rcount=0;
w = 1;
if(sign == 0){
printf("缓冲区空,等待写者\n");
return;
}
else{
printf("读者已经读完\n");
for(int i=0;i<=wq.index;i++){ // 检查写者等待队列
if(wq.writers[i]!=0){
w = 0;
printf("等待的写者%d正在写\n",wq.writers[i]);
wq.writers[i]=0;
wcount--;
break;
}//if
}//for
}//else
}//else
}
//******************************************写者优先唤醒
void WFwakeup(){
int i = 0;
int j = 0;
int m;
m = rq.index;
//n = wq.index;
if(rcount == 0){ //当前无读进程,是写者在写则停止运行写进程
bool writer_wait=false;
w=1;
printf("写者已经写完\n");
sign = 1; //缓冲区已经有内容sign要置1
for(i=0;i<=wq.index;i++){ // index为当前写者队列中的等待进程数 if(wq.writers[i]!=0){
writer_wait=true; //确实有写者在等待
printf("等待的写者%d正在写\n ",wq.writers[i]);
w = 0;
wq.writers[i]=0;
wcount--;
break;
}
}
if(!writer_wait){ //没有写者等待,看是否有读 者等待
for(int i=0;i<=m;i++){ //检查读者者等待队列
if(rq.readers[i]!=0){
w = 0;
printf("等待的读者%d正在读\n",rq.readers[i]);
rq.readers[i]=0;
rcount++;
}//if
}//for
}//if
}//if
else{ //rcount != 0有读者正在读,则停止读;此时若有等待必为写者
rcount=0;
w = 1;
printf("读者已经读完\n");
for(int i=0;i<=wq.index;i++){ //检查写者等待队列
if(wq.writers[i]!=0){
w = 0;
printf("等待的写者%d正在写\n",wq.writers[i]);
wq.writers[i]=0;
wcount--;
break;
}//if
}//for
}
}
void menu1(){
void menu();
char i;
printf(" 1-创建读者进程\n 2-创建写者进程\n 3-结束当前执行的进程\n 4-返回主菜单\n");
printf("*******************************************\n");
do{
printf("当前队列中有读者: %d个 写者: %d个\n",rq.index,wcount);
printf("*******************************************\n");
printf(" 请输入你的选择:");
scanf("%s",&i);
switch(i){
case '1':
read();
break;
case '2':
write();
break;
case '3':
RFwakeup();
break;
case '4':
menu();
default:
printf("输入错误请重新输入:");
}
}while(true);
}
void menu2(){
void menu();
char i;
printf(" 1-创建读者进程\n 2-创建写者进程\n 3-结束当前执行的进程\n 4-返回主菜单\n");
printf("*******************************************\n");
do{
printf("当前队列中有读者: %d个 写者: %d个\n",rq.index,wcount);
printf("*******************************************\n");
printf(" 请输入你的选择:");
scanf("%s",&i);
switch(i){
case '1':
read();
break;
case '2':
write();
break;
case '3':
WFwakeup();
break;
case '4':
menu();
default:
printf("输入错误请重新输入:");
}
}while(true);
}
void main(){
void menu();
menu();
}
void menu()
{
printf(" 读者写着问题 \n");
printf("**************************************************************************\n");
printf("1.读者优先唤醒\n2.写者优先唤醒\n3.退出.\n");
printf("请输入你选择的优先唤醒算法:");
scanf("%d",&choice);
while(1){
if(choice == 1)
menu1();
if(choice == 2)
menu2();
if(choice == 3)
exit(0);
if(choice != 1 && choice != 2 && choice != 3){
printf("输入错误请重新输入:");
scanf("%d",&choice);
}
}
}
22
展开阅读全文