操作系统SPOOLing系统实现课程设计心得

操作系统课程设计心得体会

 

第二篇:操作系统课程设计——Spooling假脱机输入输出模拟——附源程序

一、需求分析

设计一个SP00LING输出进程和两个请求输出的用户进程,以及一个SP00LING输出服务程序。当请求输出的用户进程希望输出一系列信息时,调用输出服务程序,由输出服务程序将该信息送入输出井。待遇到一个输出结束标志时,表示进程该次的输出文件输出结束。之后,申请一个输出请求块(用来记录请求输出的用户进程的名字、信息在输出井中的位置、要输出信息的长度等),等待SP00LING进程进行输出。SP00LING输出进程工作时,根据请求块记录的各进程要输出的信息,把信息输出到文本框里。

进程调度采用随机算法,这与进程输出信息的随机性相一致。两个请求输出的用户进程的调度概率各为45%,SP00LING输出进程为10%,这由随机数发生器产生的随机数来模拟决定。

编码实现的平台环境是JCreator4.50 Pro ,实现语言是Java。

为每个进程建立一个pcb,记录进程信息,进程有5种状态:

0为可执行态;

1为等待状态1,表示输出井满,请求输出的用户进程等待;

2为等待状态2,表示请求输出井空,SP00LING输出进程等待;

3为等待状态3,表示请求输出井满,请求输出的用户进程等待;

4为结束态,进程执行完成。

二、整体功能及设计

1、数据结构:

1)进程控制块PCB

class Pcb {

int id; 进程标识数

int status; 进程状态

int count; 要输出的文件数

int x; 进程输出时的临时变量

}

2)请求输出块Reqblock

class Reqblock {

int repname; 请求进程名

int length; 本次输出信息长度

int addr; 信息在输出井的首地址

}

3)输出井BUFFER

int buffer[3][100]

buffer[1][100]为用户进程1的输出井,buffer[2][100]为用户进程2的输出井

4)其他主要的控制变量和指针

int c1[3]; c1[1]为输出井buffer[1]的空间,c1[2]为输出井buffer[2]的空间

int c2[3][2]; c2[1][0]、c2[2][0]为输出井buffer[i]第一个空闲指针

c2[1][1]、c2[2][1]为输出井buffer[i]第一个满指针

int c3; reqblock的剩余个数

int pt1; 要输出的第一个reqblock指针

int pt2; 第一个空闲reqblock指针

2、主要类及其函数

1)主类public class Spooling

public Spooling():构造函数,生成界面,为按钮添加事件监听器 public void actionPerformed(ActionEvent e): 单击事件响应函数 public void begin(): 点击"重置"按钮时,重新初始化界面

public void run() : 点击运行时,转入调度函数

public static void main(String args[]): 生成Spooling类的对象

2)调度实施类class Manage

public Manage(Spooling spooling1) :构造函数,对进程的数据初始化 public void run() :SP00LING输出模拟系统主控函数,用随机数来决定调度的进程,使得两个请求输出的用户进程的调度概率各为45%,SP00LING输出进程为10%。

public int user(int name,int out,JTextArea textarea,JTextField field):SP00LING输出服务程序,当请求输出的用户进程希望输出一系列信息时,通过传参调用输出服务程序,由输出服务程序将该信息送入输出井。

public void spooling1() :

SPOOLING输出进程,根据请求块记录的各进程要输出的信息,把信息输出到文本框里。

1

3、流程图

(1)SP00LING输出模拟系统主控流程图如图1所示。

图1 SP00LING输出模拟系统主控流程图

操作系统课程设计Spooling假脱机输入输出模拟附源程序

2

(2)SP00LING输出服务程序流程图如图2所示。

图2 输出请求服务的程序框图

操作系统课程设计Spooling假脱机输入输出模拟附源程序

3

(3)SPOOLING输出进程流程图如图3所示。

图3 SP00LING输出进程流程图

操作系统课程设计Spooling假脱机输入输出模拟附源程序

4

三、编程实现

import java.awt.*;

import javax.swing.*;

import java.awt.event.*;

import java.lang.Math;

import java.util.Random;

/*1)主类public class Spooling

public Spooling():构造函数,生成界面,为按钮添加事件监听器 public void actionPerformed(ActionEvent e): 单击事件响应函数 public void begin(): 点击"重置"按钮时,重新初始化界面

public void run() : 点击运行时,转入调度函数

public static void main(String args[]): 生成Spooling类的对象*/

public class Spooling extends JFrame implements ActionListener,Runnable{

JPanel panel1,panel2,panel3;

JTextField field1,field2;

JScrollPane p1,p2,p3,p4;

JTextArea textarea1,textarea2,textarea3,textarea4;

JButton button1,button2,button3;

Manage spoo;

public Spooling() //界面构造函数

{

spoo=new Manage(this);

Container c=this.getContentPane();

c.setLayout(new BorderLayout());

field1=new JTextField(3); //设置panel1

field2=new JTextField(3);

button1=new JButton("运行");

button2=new JButton("关闭");

button3=new JButton("重置");

button1.addActionListener(this);

button2.addActionListener(this);

button3.addActionListener(this);

panel1=new JPanel();

panel1.setLayout(new FlowLayout());

5

panel1.add(new JLabel("用户进程1文数:",SwingConstants.RIGHT));

panel1.add(field1);

panel1.add(new JLabel("用户进程2文数:",SwingConstants.RIGHT));

panel1.add(field2);

panel1.add(button1);

panel1.add(button3);

panel1.add(button2); //设置panel1完毕

textarea1=new JTextArea(80,100); //设置panel2 textarea2=new JTextArea(112,400);

textarea3=new JTextArea(112,400);

textarea1.append("用户进程1的输出\n");

textarea2.append("用户进程2的输出\n");

textarea3.append("Spooling的调度\n");

p1=new JScrollPane(textarea3);

p2=new JScrollPane(textarea1);

p3=new JScrollPane(textarea2);

panel2=new JPanel();

panel2.setLayout(new GridLayout(1,3));

panel2.add(p1);

panel2.add(p2);

panel2.add(p3); //设置panel2完毕

textarea4=new JTextArea(10,150); //设置panel3 textarea4.append("主程序调度过程\n");

p4=new JScrollPane(textarea4);

panel3=new JPanel();

panel3.setLayout(new GridLayout(1,1));

panel3.add(p4); //设置panel3完毕

c.add(panel1,BorderLayout.NORTH); //设置窗口 c.add(panel2,BorderLayout.CENTER);

c.add(panel3,BorderLayout.SOUTH);

this.setSize(1200,600);

this.setLocation(100, 100);

this.setTitle("Spooling");

this.setVisible(true);

6 件件

this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //设置窗口完毕

}

public void actionPerformed(ActionEvent e)

{

if(e.getSource()==button1) //点击运行按钮,run() {

run();

}

if(e.getSource()==button2) //点击重置按钮,begin() {

System.exit(0);

}

if(e.getSource()==button3) //点击推出按钮,退出 {

begin();

}

}

public void begin()

{

field1.setText(""); //点击重置,重新初始化界面 field2.setText("");

textarea1.setText("用户进程1的输出\n");

textarea2.setText("用户进程2的输出\n");

textarea3.setText("Spooling的调度\n");

textarea4.setText("主程序调度过程\n");

}

public void run() //点击运行时,转入调度函数 {

spoo.start();

}

public static void main(String args[])

{

Spooling spooling=new Spooling();

}

}

7

/*2)调度实施类class Manage

public Manage(Spooling spooling1) :构造函数,对进程的数据初始化 public void run() :SP00LING输出模拟系统主控函数,用随机数来决定调度的进程,使得两个请求输出的用户进程的调度概率各为45%,SP00LING输出进程为10%。

public int user(int name,int out,JTextArea textarea,JTextField field):SP00LING输出服务程序,当请求输出的用户进程希望输出一系列信息时,通过传参调用输出服务程序,由输出服务程序将该信息送入输出井。

public void spooling1() :

SPOOLING输出进程,根据请求块记录的各进程要输出的信息,把信息输出到

文本框里。*/

class Manage extends Thread{

Pcb pcb[];

Reqblock reqblock[];

int buffer[][];

int c1[]; //可使用的输出井buffer空间

int c2[][]; //输出井buffer空闲和满指针

int c3; //reqblock的剩余个数

int pt1; //要输出的第一个reqblock指针

int pt2; //第一个空闲reqblock指针

double random; //用于调度三个进程的控制随机数 int out1; //用户进程1已生成的文件数

int out2; //用户进程2已生成的文件数

int out_1; //用户进程1已输出的文件数

int out_2; //用户进程2已输出的文件数

int x; //随机生成的数据0~9

int i; //临时控制变量

Random x1; //辅助生成随机数据x:0~9

Spooling spooling;

public Manage(Spooling spooling1) { //对各进程的数据初始化 out1=0;out2=0;

out_1=0;out_2=0;

pcb = new Pcb[4];

reqblock = new Reqblock[10];

buffer = new int[3][100];

c1=new int[3];

c1[1]=100;c1[2]=100;

c2=new int[3][2];

c2[1][0]=0;

c2[2][0]=0;

c3=10;

pt1=0;

pt2=0;

8

x1=new Random();

for(i=0;i<4;i++)

{

pcb[i]=new Pcb();

}

for(i=0;i<10;i++)

{

reqblock[i]=new Reqblock();

}

for(i=1;i<=3;i++)

{

pcb[i].status=0;

}

spooling=spooling1; //对各进程的数据初始化完毕

}

public void run() //进程调度

{

do //while循环

{

random=Math.random(); //产生一个随机数,控制进程调度,令用户进程概率为45%,Spooling进程为10%

if(random<=0.45&&pcb[1].status==0) //调度用户进程1 {

spooling.textarea4.append("调度用户进程1\n"); try {

sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

out1=user(1,out1,spooling.textarea1,spooling.field1);

}

else if(random>0.45&&random<=0.9&&pcb[2].status==0) //调度用户进程2

{

spooling.textarea4.append("调度用户进程2\n"); try {

sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

out2=user(2,out2,spooling.textarea2,spooling.field2);

9

}

else if(random>=0.9&&random<1&&pcb[3].status==0) //调度spooling进程

{

spooling.textarea4.append("调度Spooling进程\n"); try {

sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

spooling1();

}

}

while(pcb[1].status!=4||pcb[2].status!=4||pcb[3].status!=4); // while结束

spooling.textarea4.append("程序运行完毕\n"); //进程调度结束 }

public int user(int name,int out,JTextArea textarea,JTextField field) //用户进程

{

pcb[name].id =name;

pcb[name].count = Integer.parseInt(field.getText());

while(out!=pcb[name].count) //判断进程所要输出的文件是否输出完毕的while循环

{

c2[name][1]=c2[name][0];

do //判断进程的一个文件是否输出完毕的while循环 {

x=x1.nextInt(9); //x为每次随机生成的数据0~9,送入pcb.x pcb[name].x =x;

if(c1[name]==0) //若输出井buffer满,变为等待状态1,转调度程序

{

pcb[name].status=1;

if(c2[name][0]>=c2[name][1])

c1[name]=c1[name]+c2[name][0]-c2[name][1]; else

c1[name]=c1[name]+100-c2[name][1]+c2[name][0]; c2[name][0]=c2[name][1];

textarea.append("第"+(out+1)+"个文件缺少输出井,"); textarea.append("进入等待状态1\n");

10

try {

sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

return out;

}

else //若输出井没满

{

buffer[name][c2[name][0]]=pcb[name].x; //进程的输出信息PCB[i].x送buffer[i][C2[i][0]]

c1[name]=c1[name]-1; //输出井空闲个数减1 c2[name][0]=(c2[name][0]+1)%100; //修改空缓冲区指针C2[i][0]前进1

}

}while(x!=0); //判断进程的一个文件是否输出完毕的while循环结束

textarea.append("第"+(out+1)+"个文件已放入输出井:"+c2[name][1]+"~"+(c2[name][0]-1)+" 剩余空间"+c1[name]+"。 ");

try {

sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

out++;

if(c3==0) //若没有空闲请求输出块,转为等待状态3 {

pcb[name].status=3;

if(c2[name][0]>=c2[name][1])

c1[name]=c1[name]+c2[name][0]-c2[name][1];

else

c1[name]=c1[name]+100-c2[name][1]+c2[name][0];

c2[name][0]=c2[name][1];

out--;

textarea.append("缺少请求输出块,");

textarea.append("进入等待状态3。\n");

try {

sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

out++;

11

return out;

}

else //若有空闲请求输出块

{

reqblock[pt2].addr=c2[name][1];//将文件在输出井的位置填入空闲请求块

if(c2[name][0]>=c2[name][1]) //将文件在输出井的长度填入空闲请求块

reqblock[pt2].length=c2[name][0]-c2[name][1];

else

reqblock[pt2].length=100-c2[name][1]+c2[name][0]; reqblock[pt2].repname=name; //将进程名i填入请求块 textarea.append("获得请求输出块"+(Integer.toString(pt2+1))+"\n");

pt2=(pt2+1)%10; //修改空闲请求块指针

c3--; //空闲请求块数减1

if(pcb[3].status==2) //若SPOOLING进程是等待状态,则唤醒SPOOLING进程

{

pcb[3].status=0;

}

}

} //判断进程所要输出的文件是否输出完毕的while循环结束 textarea.append("进程"+name+"输出完毕!");//文件输出完毕,修改状态为结束,转进程调度

pcb[name].status=4;

return out;

}

public void spooling1()

{

while(c3!=10) //判断请求输出块是否为空的while循环 { //若请求输出块不为空

StringBuffer buffer1=new StringBuffer(100);

for(i=0;i<reqblock[pt1].length;i++) //按该请求输出信息块reqlock[]的指针ptrl将输出井中的一个文件的内容放入临时buffer1中

{

buffer1.append(buffer[reqblock[pt1].repname][reqblock[pt1].addr]);

reqblock[pt1].addr=(reqblock[pt1].addr+1)%100;

}

12

if(reqblock[pt1].repname==1)

{

out_1++;

spooling.textarea3.append("输出进程1第"+out_1+"个文件的内容:");

}

else

{

out_2++;

spooling.textarea3.append("输出进程2第"+out_2+"个文件的内容:");

}

spooling.textarea3.append(buffer1.toString()+"\n"); try {

sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

//释放相应输出井,即修改相应的输出井计数c1

c1[reqblock[pt1].repname]=c1[reqblock[pt1].repname]+reqblock[pt1].length;

pt1=(pt1+1)%10;

c3++;

int k;

for(k=1;k<=2;k++)

{

if(pcb[k].status==1)//有等待输出井的进程,唤醒相应进程,转进程调度

{

pcb[k].status=0;

return;

}

}

for(k=1;k<=2;k++)

{

if(pcb[k].status==3) //有等待请求输出块的进程,唤醒相应进程 {

pcb[k].status=0;

return;

}

13

}

} //判断请求输出块是否为空的while循环结束

if(pcb[1].status==4&&pcb[2].status==4)//进程1、2结束后输出进程结束

{

pcb[3].status=4;

spooling.textarea3.append("Spooling输出进程结束"); return;

}

else //进程等待

{

pcb[3].status=2;

return;

}

}

}

class Pcb {

int id; //进程标识数

int status; //进程状态

int count; //要输出的文件数

int x; //进程输出时的临时变量

}

class Reqblock {

int repname; //请求进程名

int length; //本次输出信息长度

int addr; //信息在输出井的首地址

}

四、使用说明

运行界面如下图1所示。

14 输出

图1 运行结果

1、在“用户进程1文件数”和“用户进程2文件数”的文本框里填入要输出的文件数。

2、点击“运行”按钮开始运行。

3、最底下的“主程序调度过程”文本区会显示每次调度的进程。

4、左边的“Spooling的调度”文本区会把请求输出块的指针所示的信息输出。

5、中间的“用户进程1的输出”文本区会显示用户进程1每个文件的占用输出井和请求输出块的情况。

6、右边的“用户进程2的输出”文本区会显示用户进程2每个文件的占用输出井和请求输出块的情况。

7、点击“重置”按钮,回到最初界面。

8、点击“关闭”按钮,关闭运行窗口。

五、结果分析

在“用户进程1文件数”和“用户进程2文件数”的文本框里分别填入3和11,点击“运行”按钮,得到的结果如上图1所示,每次的调度都用“******”隔开,从图中的数据可以分析出运行的顺序为:

1、主程序选择调度用户进程2,“用户进程2的输出”显示第1~10个文件成功得到输出井资源buffer[2][0~71]和请求输出块资源reqblock[0~9]。第11个文件缺少reqblock,申请失败,进入等待状态3,转调度函数。

2、主程序选择调度用户进程1,“用户进程1的输出”显示第1个文件缺少reqblock,申请失败,进入等待状态3,转调度函数。

3、用户进程1、2都处于等待状态3,主程序只能选择调度Spooling进程,

操作系统课程设计Spooling假脱机输入输出模拟附源程序

15

“Spooling的调度”显示第1~10个reqblock所指的信息,即用户进程2的第1~10个文件,唤醒等待输出井和请求块的进程,用户进程1、2都变为执行状态0。

4、主程序选择调度用户进程1,“用户进程1的输出”显示第1~3个文件成功得到输出井资源buffer[1][0~23]和请求输出块资源reqblock[0~2]。进程1输出完毕,转调度函数。

5、主程序选择调度用户进程2,“用户进程2的输出”显示第11个文件成功得到输出井资源buffer[2][72~77]和请求输出块资源reqblock[3]。进程1输出完毕,转调度函数。

6、用户进程1、2都处于结束状态4,主程序只能选择调度Spooling进程,“Spooling的调度”显示第1~4个reqblock所指的信息,即用户进程1的第1~3个文件和用户进程2的第11个文件。Reqblock为空且用户进程1、2都结束,所以Spooling进程变成结束状态4,转调度函数。

7、用户进程1、2和Spooling进程都处于结束状态4,主程序结束,“主程序调度过程”中显示“程序运行完毕”。

由以上分析,可知程序能够正确运行。

六、总结

在主类public class Spooling中用了 Spooling()构造函数来生成界面,actionPerformed(ActionEvent e)来响应单击事件,分别调用begiin()和run()进行重置和运行操作。其run()函数中运用了Manage类的对象spoo,所以实际上三个流程图(主控流程图、SP00LING输出服务程序流程图、SPOOLING输出进程流程图)都是在类 Manage(Spooling spooling1)中实现。

Manage(Spooling spooling1)中除了给进程数据初始化的构造函数外,有三个函数public void run() 、user()、spooling1()。

其中run() :SP00LING输出模拟系统主控函数,采用生成一个0~1的随机数,若random<0.45,则用户进程1调度user(),若0.45<=random<0.9,则用户进程2调度user(),若random>0.9,则Spooling输出进程调度spooling1(),以此使得两个请求输出的用户进程的调度概率各为45%,SP00LING输出进程为10%。

user()函数将用户信息送入输出井和输出请求块。当出现输出井满和输出请求块缺时,怎么处理是个瓶颈,通过多种方法后,直接用回滚策略,就是把此文件已放入输入井的内容忽略掉,把输出井的有关参数修改成上个文件状态。

spooling1()函数是根据请求块记录的各进程要输出的信息,把信息输出到文本框里。在user()完成后就好办多了,只要根据输出请求块中的信息响应的buffer输出,修改buffer中的控制记录变量值,有等待请求块或输出井的进程修改状态为0可执行状态,返回即可。

16

相关推荐