微智科技网
您的当前位置:首页操作系统课程设计——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所示。

开始 对各进程的PCB、输出请求快、输出井初始化 生成(0-1)随机数X 判断X的值及进程的状态 X≤0.45且进程1为执行态 0.450.9程为执行态 且SPOOLING进执行请求输出用户进程user(1) 执行请求输出用户进程user(2) 执spooling() 行SPOOLING进程N进程都结束了吗? Y结束返回

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

2

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

开始 调用进程PCB[i]的随机输出量送Y输请求输出的进程等待(1送 PCB[i].STATC1[i]= 转进程调修改空缓冲区指针C2[i][0]= 进程的输出信息出井满 NPCB[i].x送N调用进程的一个文件,输出结束了吗? YY还有空闲请求块将文件在输出井的位置和长度填入空闲请求块,将进程名i填请求输出的进修改空闲请求块指针 程等待,3送N 空闲请求块数减1 转进程调NSPOOLING进程是等待状Y唤醒SPOOLING进程 N 进程所以输出文件全部输出Y进程i运行结束 转进程调度

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

3

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

开始 YY请求输出块空两个请求输出的进程结束了YY按该请求输出信息块reqlock[]的指针ptrl将输出井中的一个文件的内容输出 SPOOLING进程等待 NSPOOLING进程结束 N释放相应输出井,即修改相应的输出井计数:按请求输出块中进程号修改C1数组和C2数组 返回 返回 Y有等待输出井空的进程吗? YN释放该请求输出块,修改请求信息块reqlock[]的指针ptrl=ptrl=1,C3=C3+1 唤醒相应转进程调N有等待输出块的进程YY唤醒相应转进程调

图3 SP00LING输出进程流程图

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文数:\

panel1.add(field1); panel1.add(new JLabel(\"用户进程2文数:\

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{

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进程,

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

因篇幅问题不能全部显示,请点此查看更多更全内容