电子科技大学---操作系统课程实验(二)

来源:互联网 发布:接单软件 编辑:程序博客网 时间:2024/06/07 01:57

前一篇文章贴出的是基本的实验思路以及要求,接下来我们用代码具体实现整个进程管理过程。由于本人比较喜欢用java开发,并且java写起来比c快多了,于是就写了一个java版的。思想都是一样的,语言不重要

系统目录:

整个系统主要是三个java代码文件:![这里写图片描述](http://img.blog.csdn.net/20170514183939198?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc3ByaW5nY29kZXI=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)

代码分析

1.Main.java

public class Main {    private static Scanner scanner =new Scanner(System.in);    private static String command=null;    public static void main(String args[]){        command=scanner.nextLine();        command=command.trim();        while(command!=""){            TestShell.exeCommand(command);            command=scanner.nextLine();            command=command.trim();        }    }}

主函数中没有什么,就是对控制面板输入的命令进行处理

2.PCB.java

PCB(process control block),进程控制块,是我们学习操作系统后遇到的第一个数据结构描述,它是对系统的进程进行管理的重要依据,和进程管理相关的操作无一不用到PCB中的内容。一般情况下,PCB中包含以下内容:
(1)进程标识符(内部,外部)
(2)处理机的信息(通用寄存器,指令计数器,PSW,用户的栈指针)。
(3)进程调度信息(进程状态,进程的优先级,进程调度所需的其它信息,事件)
(4)进程控制信息(程序的数据的地址,资源清单,进程同步和通信机制,链接指针)

由于这个试验中用不到这么多信息,所以我们的代码中也就没有写这么多。主要包括:进程名,进程ID,父进程,优先级,子进程等的一些简要信息

    private int pid;//正在运行的process id    private int prioirty;    private String name;    private String childName=null;    private Map<String ,Integer> requestRes=new HashMap<>();    private int parentId;    private int childId;    private int state;

其中requestRes表示进程请求的资源,初始化的时候都是0,表示进程刚刚创建,还没有请求资源

此外我们在定义几个表示进程状态的常量:

    public static final int STATE_RUNNING=0;    public static final int STATE_BLOCKED=1;    public static final int STATE_READY=2;    public static final int STATE_DESTORYED=3;

3.Log.java
由于需要输出一些文字,包括一些必要的进程信息,还有命令处理的出错信息,其实就是嫌弃System.out.println(“”)太长了,这里我们写一个工具类,便于开发

public class Log {    public static void e(String info){        System.out.println(info);    }    public static void e(String tag,String info){        System.out.println(tag+":"+info);    }}

4.TestShell.java
接下来就是最重要的TestShell.java 了。在这个类中,我们首先将得到的命令进行解析,在解析的时候要注意一下几点:
(1)命令的单词之间只能有一个空格,并且只能是空格
(2)命令解析到具体的命令的时候需要对命令的长度进行判断,如果命令的参数个数不符合要求,则给出合理的提示,包括命令的使用方法
(3)其次在进行参数的正确性判断,对不符合规定的参数进行提示,并给出正确参数的提示,便于用户改正错误
(4)用户的第一条命令只能是init,用于创建系统的init进程(为什么这么干,额实验老师就是这么要求的)

接下来就是具体的命令的解析以及执行了

首先先定义两个队列,存放就绪进程跟阻塞进程:

    private static List<PCB> ready=new ArrayList<>();    private static List<PCB> blocked=new ArrayList<>();

其次是定义一个系统资源队列,这里文档中要求的资源 以及个数如下:
R1—1个
R2—2个
R3—3个
R4—4个

private static Map<String ,Integer> avaliableRes=new HashMap<>();

在init命令执行的时候进行系统资源的初始化,也可以将此代码写在静态代码块中

 // 初始化资源   avaliableRes.put("R1",1);   avaliableRes.put("R2",2);   avaliableRes.put("R3",3);   avaliableRes.put("R4",4);

同时创建一个init进程,他的优先级是0,为系统级进程,同时由于还没有子进程,所以我们将他的子进程id设置成-1
整个代码如下:

 /**     * init 进程启动     */    public static void init(){        PCB initPrecess=new PCB(0,-1,"init",0);        initPrecess.setChildId(-1);        //ready.add(initPrecess);        // 初始化资源        avaliableRes.put("R1",1);        avaliableRes.put("R2",2);        avaliableRes.put("R3",3);        avaliableRes.put("R4",4);        //从ready中拿到一个进程开始执行,也就是init进程        currentProcess=initPrecess;    }

创建新进程:
首先创建一个新的进程,他的父进程就是当前正在运行的进程,我们从当前进程中得到他的父进程的信息,同时改写当前进程的childId,将他设置成当前进程的id。同时将这个新建的进程放在就绪队列中准备被调度

/**     * 创建新进程,并将父进程与子进程联系起来,     * 创建后进入就绪队列,不进行调度     * @param parentId     * @param name     * @param priority     */    public static void createNewTread(int parentId,String name,int priority){        pid++;        PCB pcb=new PCB(pid,parentId,name,priority);        pcb.setChildId(-1);        currentProcess.setChildId(pid);        currentProcess.setChildName(name );        ready.add(pcb);    }

进程的阻塞:
进程在请求资源的时候如果系统剩余的资源不够,那么进程将进行阻塞,这个时候如果进程已经得到了一部分 资源,那么这部分资源他将不会释放,也就是带着资源进行阻塞。一个正在运行中的进程阻塞的时候,系统除了将他调入阻塞队列中,还会从就绪队列中调出一个优先权最高的进程执行,如果优先级都是一样的,那么调度最先进入队列的那个进程:

 /**     * 正在运行的进程请求分配资源     * @return true:成功 false 资源不够失败     */    public static boolean allocateRes(String name,int num){        int availNum=avaliableRes.get(name)-num;        Map curPossMap=currentProcess.getRequestRes();        if (availNum<0){            return false;        }        int possNum= (int) curPossMap.get(name);        curPossMap.put(name,num+possNum);        avaliableRes.put(name, availNum);        return  true;    }

如果没有成功,则从ready队列中调用一个就绪进程

  if(!success){                    System.out.println("no enough res");                    System.out.println("current process --name :"+currentProcess.getName()+" blocked");                    blockCurrentProcess();                    getProcessFromReady(3);                }

时间片的切换:
当一个进程的时间片用完的时候需要切换时间片,这个时候需要将当前的进程调入就绪队列中,然后在从就绪队列中调出一个优先级最高的进程执行

 /**     * 切换进程     */    private static void timeOut(){        //不释放资源        getProcessFromReady(2);    }

进程的结束:
当进程被结束或者是正常的结束的时候,首先会释放自己已有的资源,同时如果这个进程是正在运行的进程,系统还会从就绪 队列中调一个进程执行,同时进程的结束是一个递归的过程,被结束的进程还会结束他的子进程。当有进程被结束就可能有资源被释放出来,所以在此同时,我们还需要对资源进行回收,回收的资源如果阻塞队列中有进程,那么就需要判断资源能否满足进程的需求,如果能满足则需要将资源分配给进程。同时将进程调度到就绪队列中,在从就绪队列中调出应该得到时间片的那个进程。所以这是一个复杂的工作

 public static boolean destoryThread(String name){        PCB delProcess=null;        boolean find=false;        /**         * 删除ready中的         */        for (Iterator<PCB> it = ready.iterator(); it.hasNext();) {            PCB value = it.next();            if (value.getName().equals(name)) {                delProcess=value;                find=true;                it.remove();                continue;            }        }        /**         * 删除blocked中的         */        if (!find){            for (Iterator<PCB> it = blocked.iterator(); it.hasNext();) {                PCB value = it.next();                if (value.getName().equals(name)) {                    delProcess=value;                    find=true;                    it.remove();                    continue;                }            }        }        /**         * 删除正在运行中的         */        if(currentProcess.getName().equals(name)){            delProcess=currentProcess;            find=true;        }        if(!find){            return false;        }        /**         * 释放资源         */        if(delProcess!=null){            Map resMap =delProcess.getRequestRes();            releaseProceeRes(resMap);            changeBlockState();            //如果删除的是当前正在运行的进程            if(delProcess.getPid()==currentProcess.getPid()){                currentProcess.setState(PCB.STATE_DESTORYED);            }        }        if(delProcess.getChildId()>0){            destoryThread(delProcess.getChildName());        }        return true ;    }

进程的调出:
那么这里就有一个很严肃的问题了,那就是如何从就绪队列中调出该得到时间片的进程了。这里分成三类,一个是直接从就绪队列中调出一个应该执行的进程接下来执行,这种情况为阻塞发生的时候,已经进程被结束的时候。还有一种是需要将当前正在运行的进程放入就绪队列在进行调出的。这里我们用数字2,3,4分开这三种情况
2—timeout 的时候调用,一定从ready中取一个进程,再将当前进程调入就绪队列
3—当前进程被阻塞的时候一定从ready中取一个,当前进程进入阻塞队列
4—当前进程被结束的时候,一定从ready中取出一个进程

 * 从一个队列中得到process分配cpu执行     * @param  :1 readyList  2:blockedList     * @return 1 优先ready队列     */    private static void  getProcessFromReady(int i ){        if(ready.size()==0){            Log.e("first","return");            return ;        }        //寻找ready中优先级最高的进程        PCB cProcess=null;        int maxProirity=-1;        for (Iterator<PCB> it = ready.iterator(); it.hasNext();) {            PCB value = it.next();            if(value.getPrioirty()>maxProirity){                cProcess=value;                maxProirity=value.getPrioirty();            }        }        switch (i){                //timeout 的时候调用,一定从ready中取一个进程            case 2:                if(cProcess!=null){                    if(cProcess.getPrioirty()<currentProcess.getPrioirty()){                        return;                    }                    ready.add(currentProcess);                    currentProcess=cProcess;                    ready.remove(cProcess);                    return;                }                break;                //当前进程被阻塞的时候一定从ready中取一个            case 3:                blocked.add(currentProcess);                currentProcess=cProcess;                ready.remove(cProcess);                break;                //当前进程被结束的时候            case 4:                currentProcess=cProcess;                ready.remove(cProcess);                break;        }    }

当资源被释放的时候我们又需要对阻塞队列中的进程进行调度了:

1.释放当前进程的资源:

 /**     *     * @param name 资源名     * @param num 数目     * @return 是否成功释放     */    public static boolean relCurProRes(String name,int num){        Map curResMap=currentProcess.getRequestRes();        int n = (int) curResMap.get(name);        if(num>n){            System.out.println("res release too much for possess");            return false;        }        avaliableRes.put(name,avaliableRes.get(name)+num);        curResMap.put(name,n-num);        return true;    }

2.对阻塞队列中的进程进行调度

    /**     * 当一个进程被结束掉之后,释放资源,对释放后的资源进行判断,     * 如果该资源能够让阻塞队列中的process 激活则激活并进入ready队列     */    public static boolean changeBlockState(){        for(int i =0;i<blocked.size();i++){            PCB p=blocked.get(i);            //如果系统剩余资源够用,则为阻塞队列的process分配资源,并将改process 移到ready中            if((avaliableRes.get("R1")>=p.getRequestRes().get("R1"))&&                    (avaliableRes.get("R2")>=p.getRequestRes().get("R2"))&&                    (avaliableRes.get("R3")>=p.getRequestRes().get("R3"))&&                    (avaliableRes.get("R4")>=p.getRequestRes().get("R4"))                    ){                ready.add(p);                blocked.remove(p);            }        }        return true;    }

其实很容易看出来这个函数里面写错了,少了减少剩余系统资源的部分,由于老师给的例子不涉的特殊性,本人暂时没时间改了,就暂时不弄了,管他的,先交报告,错误的地方请读者自行改正。

在此整个进程调度的算法就基本完成,同时还有一些命令的解析函数没有贴上来,感觉太多了。需要整个项目的可以自行下载:

URL=http://download.csdn.net/detail/springcoder/9842209

注意:这里 在队列中删除item的时候用了迭代器,读者可以试试不用迭代器,这样的话会报错。主要原因是因为list没有加锁的原因,大家也可以试试对list进行加锁访问

转载请注明地址:http://blog.csdn.net/springcoder/article/details/72049041

0 0