操作系统课设——设计模拟一个SPOOLING假脱机输出程序

本报告详细介绍了广州大学操作系统课程设计中的SPOOLING假脱机输出程序设计,包括系统设计要求、进程调度策略、状态转换规则及数据结构设计。通过随机调度算法模拟了两个用户进程与SPOOLING输出进程的交互,展示了请求输出服务程序和SPOOLING输出进程的流程,以及程序源代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

广州大学操作系统课程设计报告

要求:书写课程设计报告,报告中应该包含如下内容:
一.课程设计题目及内容
课程设计题目:题目三: 设计模拟一个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.我觉得操作系统课程设计放在期末考试前是一种不妥的行为,一方面学生要忙于复习,另一方面又要忙于课程设计。既影响复习也影响课设完成的质量。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值