Bootstrap

【原创】操作系统课程设计

题目:

实现对SPOOLING输出模拟系统。具体实现要求如下:(不局限下述情况,在工作量不缩减的情况下,可行设计实现过程。)

(1)包括一个SPOOLING输出进程和一个SPOOLING输出请求服务程序以及两个请求输出的进程。

(2)请求输出进程每次调用请求服务程序输出一行信息,由输出请求服务程序将该信息项送如输出井。待输出一个结束标志时,表示一个输出文件输出完成,在输出井中形成一个输出信息块,并构成一个输出请求块,SPOOLING输出进程工作时,根据某输出请求块将输出井中相应信息块实际输出到打印机或CRT,SPOOLING进程与请求输出进程可并发运行。

(3)进程调度采用随机调度法,这与进程的输出的随机性相一致。两个请求输出进程的调度概率各为45%,SPOOLING输出进程为10%,这由产生随机数来模拟。

(4) 为进程设置三种基本运行状态:可执行、等待和结束。等待状态又可分成等待状态1和等待状态2。状态变换的条件如下:

①进程执行完毕后置成“结束态”。

②服务程序在输出信息时,如发现输出井已满,将调用进程置成“等待状态1”。

③SPOOLING进程在输出井空时进入“等待状态2”。

④SPOOLING进程输出一个信息块后,应释放该信息块所占用的输出井空间,并将正在等待输出的进程置为“可执行态”。

⑤ 服务程序在输出信息到输出井并形成信息块后,将SPOOLING进程置成“可执行态”。

流程图分析:

(1)进程调度工作模拟流程:

(2)请求输出进程流程

(3)SPOOLING输出进程流程

实现代码:

#include <iostream>
#include "stdio.h"
#include "stdlib.h"
#include "time.h"
using namespace std;

#define PROCESSNUM 2   //两个请求输出的进程
#define BUFFERNUM  100  //内存缓冲字节个数
#define OUTBUFFERNUM  200  //输出井存储字节个数
#define REQBLOCKNUM   10   //定义请求块个数


//0:可执行态:SPOOLING进程输出一个信息块后,应释放该信息块所占用的输出井空间,并将正在等待输出的进程置为“可执行态”。;
//服务程序在输出信息到输出井并形成信息块后,将SPOOLING进程置成“可执行态”
//1:等待状态1,表示输出井满,请求输出的用户进程必须等待,将调用进程设置为“等待状态1”。
//2:等待状态2,表示输出井空,SPOOLING输出进程必须等待
//3:等待状态3,表示输出请求块使用完,请求输出的用户进程必须等待
//4:结束态:进程执行完毕

struct pcb //进程控制块PCB
{
	int id;		//进程标识 
	int status;	//状态 
	int length;//输出长度 
	int buf[BUFFERNUM];//输出缓冲 
}PCB[PROCESSNUM + 1];

struct req   //请求输出块
{
	int reqid;//要求输出的进程 
	int length;//输出长度 
	int addr;//输出首地址 
}ReqBlock[REQBLOCKNUM];

struct outbuf  //输出井结构
{
	int buf[OUTBUFFERNUM];   //输出井缓冲区
	int usedNum;     //输出井缓冲区已使用的数目
	int head;  //指示输出井空闲块首地址
	//int tail;  //指示输出井信息块(有信息的部分)尾地址
}OutBuffer[PROCESSNUM];

int UsedReqBlockNum = 0;   //记录当前已使用的请求块数目
int head = 0, tail = 0;  //指示当前使用的输出请求块,request从tail开始取,spooling从head开始取
int FileNum[PROCESSNUM];

void input()//输入函数 
{
	for (int i = 0; i < PROCESSNUM; i++)
	{
		cout << "输入第" << i + 1 << "个用户需要输出的文件数目:";
		cin >> FileNum[i];
	}
}

void init()//初始化函数 
//对各进程的PCB、输出请求块、输出并初始化
{
	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;
		for (j = 0; j < BUFFERNUM; j++)
			PCB[i].buf[j] = 0;
	}
	PCB[PROCESSNUM].status = 2;  //spooling进程的状态置2(输出井空)
}


void request(int i)  //请求输出进程流程
{
	int j, length = 0;

	if (PCB[i].length == 0)   //判断上次的输出是否处理完
	{
		FileNum[i] = FileNum[i] - 1;

		
		while (1)
		{
			j = rand() % 10;//随机数
			if ((j == 0) && (length != 0))    //以0结束此次输出
			{
				PCB[i].length = length;
				break;
			}
			PCB[i].buf[length] = j;
			length++;
		}
	}

	if (OutBuffer[i].usedNum + length > OUTBUFFERNUM)   //判断输出井是否满
	{
		PCB[i].status = 1;   //等待状态1,表示输出井满,请求输出的用户进程必须等待
		return;
	}

	if (UsedReqBlockNum == REQBLOCKNUM)    //判断是否有空闲的请求块
	{
		PCB[i].status = 3;  //没有空闲的请求块,进程状态置3,
		return;
	}

	//填写请求块
	ReqBlock[tail].reqid = i;
	ReqBlock[tail].addr = OutBuffer[i].head;
	ReqBlock[tail].length = PCB[i].length;
	UsedReqBlockNum++;

	//将数据写到输出井
	int k;
	for (k = 0; k < PCB[i].length; k++)
		OutBuffer[i].buf[(OutBuffer[i].head + k) % OUTBUFFERNUM] = PCB[i].buf[k];
	OutBuffer[i].head = (OutBuffer[i].head + PCB[i].length) % OUTBUFFERNUM;
	OutBuffer[i].usedNum += PCB[i].length;
	PCB[i].length = 0;

	if (PCB[PROCESSNUM].status == 2)  //若spooling进程阻塞,则修改其状态为可执行(0)就绪状态

		PCB[PROCESSNUM].status = 0;
	tail = (tail + 1) % REQBLOCKNUM;

	if (FileNum[i] == 0)
		PCB[i].status = 4;
}

void spooling()  //spooling输出进程流程
{
	//完成spooling函数的设计
	if (UsedReqBlockNum == 0) {//如果没有请求块
		if (PCB[0].status == 4 && PCB[1].status == 4) {//是否所有输出进程结束
			PCB[2].status = 4;
			return;
		}
		else {
			PCB[2].status = 2;
			return;
		}
	}
	//按照请求块从输出井中取数据输出(打印到屏幕)
		//遍历请求块
	int requid = ReqBlock[head].reqid;
	int addr = ReqBlock[head].addr;
	int length = ReqBlock[head].length;
	UsedReqBlockNum--;
	//将数据从输出井输出

	cout << "以下为输出结果:" << endl;
	int k;
	for (k = 0; k < length; k++)
		cout << OutBuffer[requid].buf[(addr + k) % OUTBUFFERNUM] << " ";
	cout << endl;
	OutBuffer[requid].usedNum -= length;

	if (PCB[0].status == 3)  //修改阻塞进程状态为就绪
		PCB[0].status = 0;
	if (PCB[1].status == 3)
		PCB[1].status = 0;
	head = (head + 1) % REQBLOCKNUM;



}

void work()  //模拟进程调度 
//进程调度采用随机调度法,这与进程的输出的随机性相一致
{
	int i;
	bool isFinish;

	srand((unsigned)time(NULL));
	while (1)
	{
		i = rand() % 100;
		if (i <= 45)
		{
			if (PCB[0].status == 0)
				request(0);
		}
		else if (i <= 90)
		{
			if (PCB[1].status == 0)
				request(1);
		}
		else
			spooling();

		isFinish = true;
		for (i = 0; i < PROCESSNUM + 1; i++)  //判断是否所有进程都结束
			if (PCB[i].status != 4)
				isFinish = false;
		if (isFinish)     //若所有进程都结束,则退出
			return;
	}
}
int main() //主程序
{
	printf("\n>>>>>>>>>>>>>>>> SPOOLing系统模拟程序 <<<<<<<<<<<<<<<<<\n");

	init();
	input();
	cout << "Spooling技术将会随机从1~9分配给每个文件的缓冲区,并按照请求块的顺序将其打印" << endl;
	work();

	return 0;

}

解释说明:

0:可执行态:SPOOLING进程输出一个信息块后,应释放该信息块所占用的输出井空间,并将正在等待输出的进程置为“可执行态”;服务程序在输出信息到输出井并形成信息块后,将SPOOLING进程置成“可执行态”
1:等待状态1,表示输出井满,请求输出的用户进程必须等待,将调用进程设置为“等待状态1”。
2:等待状态2,表示输出井空,SPOOLING输出进程必须等待
3:等待状态3,表示输出请求块使用完,请求输出的用户进程必须等待
4:结束态:进程执行完毕

输出结果:

;