广州大学操作系统课程设计报告
要求:书写课程设计报告,报告中应该包含如下内容:
一.课程设计题目及内容
课程设计题目:题目三: 设计模拟一个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要输出的文件数T2
struct 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; //没有空闲的请求块,进程状态置3
cout << "没有空闲的请求块,进程状态置1" << endl;
return;
}
C3--;//申请一个空闲的请求输出块
//判断输出文件的字符数是否大于20,如果大于20,标志aa和bb会置为false
bool aa = true;
bool bb = true;
if (i == 0) //如果是用户进程0
for (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; //修改的输出请求块的个数加1
PCB[0].status = 1; //挂起进程
return;
}
}
else { //如果是用户进程1
for (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; //修改的输出请求块的个数加1
return;
}
}
}
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已经输出的文件个数
}
else
ReqBlock[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已经输出的文件个数
}
else
ReqBlock[n_in].length = 20;
}
//填写请求块
ReqBlock[n_in].reqid = i; //置该输出请求块的进程名字为i
ReqBlock[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.我觉得操作系统课程设计放在期末考试前是一种不妥的行为,一方面学生要忙于复习,另一方面又要忙于课程设计。既影响复习也影响课设完成的质量。