一、需求分析
设计一个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.45 图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
因篇幅问题不能全部显示,请点此查看更多更全内容