广州大学操作系统课程设计报告
要求:书写课程设计报告,报告中应该包含如下内容:
一.课程设计题目及内容
课程设计题目:题目三: 设计模拟一个SPOOLING假脱机输出程序
(1) 系统设计要求:设计一个SPOOLING输出进程和两个请求输出的用户进程,以及一个SPOOLING输出服务程序request。当用户进程希望输出一系列信息时,调用SPOOLING输出服务程序request,由输出服务程序将该信息送入输出井。待给出一个结束标志时,表示进程该次的文件输出结束。之后,申请一个输出请求块,用来记录请求输出的用户进程的名字、要输出的文件名以及要输出信息的长度等。等待SPOOLING输出进程进行输出。这里,SPOOLING输出进程与请求输出的用户进程可并发运行。SPOOLING输出进程工作时,根据请求块记录的各进程要输出的信息,将其实际输出到打印机或显示器,这里记录到一个文件中。
(2) 进程调度:采用随机调度算法,这与进程要求输出信息的随机性相一致。两个请求输出的用户进程的调度概率各为40%,SPOOLING输出进程被调度的概率为20%,这由随机数发生器产生的随机数来模拟决定。
(3) 进程状态:进程有三个基本状态,分别为可执行、等待和结束。状态变化的条件为:
1) 当进程正在运行或等待调度时的状态为可执行态。
2) 服务程序在将用户输出信息送输出井时,如发生输出请求块已用完,将调用它的用户进程置为“等待状态1”。
3) SPOOLING输出进程在进行输出时,若发现输出请求块为空,则进入“等待状态2”。
4) SPOOLING输出进程输出一个信息块后,应将正在等待输出的进程置为“可执行状态”。
5) 服务程序在输出信息到输出井并形成输出请求信息块后,若SPOOLING进程处于等待态,则将其置为“可执行状态”。
6) 进程执行完成时,置为“结束态”。
二.程序中使用的数据结构及主要符号说明
(4) 数据结构:
1) 进程控制块(PCB)如下:
struct pcb //进程控制块PCB
{int id; //进程标识 int status; //状态0为可执行态;等待状态1,表示请求输出块用完,请求输出的用户进程等待;等待状态2,表示输出井空,SPOOLING输出进程等待;3为结束态int length;//输出长度
}PCB[PROCESSNUM + 1];
其中:status=0 为可执行态;status=1 为等待状态1,表示请求输出块用完,请求输出的用户进程等待;status=2 为等待状态2, 表示输出井空,SPOOLING输出进程等待;status=3 为结束态,进程执行完成。
2) 请求输出块reqblock 如下:
struct reqblock //请求输出块
{int reqid;//要求输出的进程 int tname;int length;//输出长度 int addr;//输出首地址
}ReqBlock[REQBLOCKNUM];
3) 输出井BUFFER。SPOOLING系统为每个请求输出的进程在输出井中分别开辟一个区。本题目可设计一个二维数组(int buffer[T1][30] 和 buffer[T2][30])作为输出井。每个进程一个文件最多可占用输出井20个位置。
struct BUFFER //输出井结构
{int buf[OUTBUFFERNUM]; //输出井缓冲区int usedNum; //输出井缓冲区已使用的数目int head; //指示输出井空闲块首地址//int tail; //指示输出井信息块(有信息的部分)尾地址
}OutBuffer[PROCESSNUM];
三.程序流程图和带有注释的源程序
SPOOLING系统输出模拟的主控流程图如图3-1所示:
SPOOLING输出服务程序request(由请求输出的两个用户进程调用)流程图如图3-2所示:
SPOOLING输出进程流程图如图3-3所示:
图3-1 SPOOLING系统输出模拟的主控流程图
图3-2 SPOOLING输出服务程序request流程图
图3-3 SPOOLING输出进程流程图
源程序:
#include "stdio.h"
#include "stdlib.h"
#include "time.h"
#include <iostream>
using namespace std;#define PROCESSNUM 2 //输出进程个数
#define OUTBUFFERNUM 30 //输出井存储字节个数
#define REQBLOCKNUM 10
#define T1 3 //定义用户进程0要输出的文件数T1
#define T2 3 //定义用户进程1要输出的文件数T2struct pcb //进程控制块PCB
{int id; //进程标识 int status; //状态0为可执行态;等待状态1,表示请求输出块用完,请求输出的用户进程等待;等待状态2,表示输出井空,SPOOLING输出进程等待;3为结束态int length;//输出长度
}PCB[PROCESSNUM + 1];struct reqblock //请求输出块
{int reqid;//要求输出的进程 int tname;int length;//输出长度 int addr;//输出首地址
}ReqBlock[REQBLOCKNUM];struct BUFFER //输出井结构
{int buf[OUTBUFFERNUM]; //输出井缓冲区int usedNum; //输出井缓冲区已使用的数目int head; //指示输出井空闲块首地址//int tail; //指示输出井信息块(有信息的部分)尾地址
}OutBuffer[PROCESSNUM];int C3 = 10; //C3表示当前系统剩余的请求输出信息块个数,初值为10
int n_out = 0, n_in = 0; //指示当前使用的输出请求块,request从n_in开始取,spooling从n_out开始取int t1 = 0; //设两个计时器,分别记录两个用户进程已经输出的文件个数,都初始化为0
int t2 = 0;
int t_num[2][10];void init()//初始化函数
{int i, j;for (i = 0; i < PROCESSNUM; i++){OutBuffer[i].head = 0;OutBuffer[i].usedNum = 0;for (j = 0; j < OUTBUFFERNUM; j++)OutBuffer[i].buf[j] = 0;}for (i = 0; i < REQBLOCKNUM; i++){ReqBlock[i].reqid = -1;ReqBlock[i].length = 0;ReqBlock[i].addr = 0;}for (i = 0; i < PROCESSNUM + 1; i++){PCB[i].id = i;PCB[i].status = 0;PCB[i].length = 0;}//自动生成用户进程0和用户进程1的文件长度/*for (i = 0; i < T1; i++){t_num[0][i] = (rand() % 30) + 1;}*//*for (i = 0; i < T2; i++){t_num[1][i] = (rand() % 30) + 1;}*///手动生成用户进程0和用户进程1的文件长度t_num[0][0] = 15;t_num[0][1] = 18;t_num[0][2] = 8;t_num[1][0] = 25;t_num[1][1] = 18;t_num[1][2] = 6;
}void request(int i)
{cout << "==============================================================================="<<endl;cout << "进程: " << i << " 调用request进程,写入的进程块序号为ReqBlock[" << n_in << "]" << endl;int j, length = 0;if (C3 == 0) //判断是否有空闲的请求块{PCB[i].status = 1; //没有空闲的请求块,进程状态置3cout << "没有空闲的请求块,进程状态置1" << endl;return;}C3--;//申请一个空闲的请求输出块//判断输出文件的字符数是否大于20,如果大于20,标志aa和bb会置为falsebool aa = true;bool bb = true;if (i == 0) //如果是用户进程0for (int k = 0; k < t_num[0][t1]; k++) {j = (rand() % 10) + 1;//随机数if (k == 20) {aa = false;cout << endl<<"该文件个数大于20,将被挂起" << endl;t_num[0][t1] = t_num[0][t1] - 20;PCB[0].status = 1;break;}OutBuffer[0].buf[(OutBuffer[i].head + k) % OUTBUFFERNUM] = j; //J送buffer[0][length]cout << j << " ";OutBuffer[0].usedNum++;//判断输出井是否满if (OutBuffer[0].usedNum == OUTBUFFERNUM) {cout << endl<<"输出井满" << endl;ReqBlock[n_in].length = k+1;t_num[0][t1] = t_num[0][t1] - ReqBlock[n_in].length;ReqBlock[n_in].reqid = i;ReqBlock[n_in].addr = OutBuffer[i].head;n_in = (n_in + 1) % REQBLOCKNUM; //修改的输出请求块的个数加1PCB[0].status = 1; //挂起进程return;}}else { //如果是用户进程1for (int k = 0; k < t_num[1][t2]; k++) {j = (rand() % 10) + 1;//随机数if (k == 20) {bb = false;cout << endl<<"该文件个数大于20,将被挂起" << endl;t_num[1][t2] = t_num[1][t2] - 20;PCB[1].status = 1;break;}OutBuffer[1].buf[(OutBuffer[i].head + k) % OUTBUFFERNUM] = j; //J送buffer[0][length]cout << j << " ";OutBuffer[1].usedNum++;//判断输出井是否满if (OutBuffer[1].usedNum == OUTBUFFERNUM) {cout << "输出井满" << endl;PCB[1].status = 1; //挂起进程ReqBlock[n_in].length = k+1;t_num[1][t2] = t_num[1][t2] - ReqBlock[n_in].length;ReqBlock[n_in].reqid = i;ReqBlock[n_in].addr = OutBuffer[i].head;n_in = (n_in + 1) % REQBLOCKNUM; //修改的输出请求块的个数加1return;}}}cout << endl;if (i == 0) {cout << "进程 " << i << " 文件的" << t1 << "的字符个数:" << t_num[0][t1] << endl << endl;if (aa) {ReqBlock[n_in].length = t_num[0][t1];cout << " <<<<<<<<<<<<<<<<<<<<<<进程结束>>>>>>>>>>>>>>>>>>>>" << endl << endl;t1++; //t1记录用户进程1已经输出的文件个数}elseReqBlock[n_in].length = 20;}else {cout << "进程 " << i << " 文件" << t2 << "的字符个数:" << t_num[1][t2] << endl << endl;if (bb) {ReqBlock[n_in].length = t_num[1][t2];cout << " <<<<<<<<<<<<<<<<<<<<<<进程结束>>>>>>>>>>>>>>>>>>>>" << endl << endl;t2++; //t2记录用户进程1已经输出的文件个数}elseReqBlock[n_in].length = 20;}//填写请求块ReqBlock[n_in].reqid = i; //置该输出请求块的进程名字为iReqBlock[n_in].addr= OutBuffer[i].head; //修改输出首地址OutBuffer[i].head = (OutBuffer[i].head + ReqBlock[n_in].length) % OUTBUFFERNUM;if (PCB[PROCESSNUM].status == 2) //若spooling进程阻塞,则修改其状态为可执行(0)PCB[PROCESSNUM].status = 0;n_in = (n_in + 1) % REQBLOCKNUM; //修改的输出请求块的个数加1}void spooling()
{//请完成spooling函数的设计if (C3 == 10) {//如果没有请求块if (PCB[0].status == 3 && PCB[1].status == 3) {//是否所有输出进程结束PCB[2].status = 3;return;}else {PCB[2].status = 2;return;}}cout << "*******************************************************************************" << endl;//按照请求块从输出井中取数据输出(打印到屏幕)//遍历请求块while (C3 < 10) {int requid = ReqBlock[n_out].reqid;int addr = ReqBlock[n_out].addr;int length = ReqBlock[n_out].length;cout << "addr" << addr << endl;cout << "SPOOLING输出进程为:" << requid << endl;cout << "调用SPOOLING进程,释放的进程块序号为ReqBlock[" << n_out << "]" << endl;cout << "以下为输出结果:" << endl;int k;if (requid == 0) {for (k = 0; k < length; k++)cout << OutBuffer[0].buf[(addr + k) % OUTBUFFERNUM] << " ";OutBuffer[0].usedNum = OutBuffer[0].usedNum - length;}else {for (k = 0; k < length; k++)cout << OutBuffer[1].buf[(addr + k) % OUTBUFFERNUM] << " ";OutBuffer[1].usedNum = OutBuffer[1].usedNum - length;}cout << endl;C3++;//将数据从输出井输出n_out = (n_out + 1) % REQBLOCKNUM;}if (PCB[0].status == 1) //修改阻塞进程状态为就绪PCB[0].status = 0;if (PCB[1].status == 1)PCB[1].status = 0;cout << "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" << endl<<endl;return;}void work()//模拟进程调度
{int i;bool isFinish;srand((unsigned)time(NULL));while (1){i = rand() % 100;if (i <= 40 && PCB[0].status == 0){if (t1 < 3) {request(0);}}else if (i <= 80 && PCB[1].status == 0){if (t2 < 3) {request(1);}}else if (i > 80 && PCB[2].status == 0){spooling();}//所有进程都结束了吗isFinish = true;if (t1 == T1) {PCB[0].status = 3;}if (t2 == T2) {PCB[1].status = 3;}for (i = 0; i < PROCESSNUM + 1; i++) {if (PCB[i].status != 3)isFinish = false;}if (isFinish) //若所有进程都结束,则退出return;}}
int main() //主程序
{srand((unsigned)time(NULL));init();cout << "\n>>>>>>>>>>>>>>>> SPOOLing系统模拟程序 <<<<<<<<<<<<<<<<<\n";cout << "进程0创建" << T1 << "个文件" << endl;for (int i = 0; i < T1; i++) {cout << "进程0文件" << i << "的文件个数是" << t_num[0][i] << endl;}cout << endl;cout << "进程1创建" << T2 << "个文件" << endl;for (int i = 0; i < T2; i++) {cout << "进程1文件" << i << "的文件个数是" << t_num[1][i] << endl;}cout << endl;work();return 0;}
四.执行程序名,并打印程序运行时的初值和运算结果
五.实验结果分析,实验收获和体会
操作系统是比较复杂,抽象的课程。单单看书,很难领会操作系统的知识。只有多动手,多做实验,才会对操作系统的知识有更深刻的体会,比如文件管理,SPOOLING技术等。纸上得来终觉浅,绝知此事要躬行。
六.实验的改进意见和建议。
1.我觉得操作系统课程设计放在期末考试前是一种不妥的行为,一方面学生要忙于复习,另一方面又要忙于课程设计。既影响复习也影响课设完成的质量。