操作系统课程设计心得体会
一、需求分析
设计一个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输出模拟系统主控流程图
2
(2)SP00LING输出服务程序流程图如图2所示。
图2 输出请求服务的程序框图
3
(3)SPOOLING输出进程流程图如图3所示。
图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文数:",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进程,
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
南通大学操作系统课程设计实验报告一设计内容利用C语言实现操作系统模拟算法和windows的系统调用编程分别设计进程调度的时间片轮转…
操作系统课程设计总结报告学期20xx20xx学年第2学期学院软件学院学号姓名20xx年7月3日本学期开设了操作系统课程主要学习了计…
操作系统课程设计报告专业学号姓名提交日期操作系统课程设计报告设计目的1本实验的目的是通过一个简单多用户文件系统的设计加深理解文件系…
操作系统课程设计题目与要求课程设计要求1可以依据教材中的算法自行选题也可以从下面给出的题目中选题要求每两名同学之间课程设计内容应该…