《操作系統之Java實現模擬多級反饋隊列進程調度(帶GUI)》


​不得不說,利用IDEA的GUI Form對Swing的支持,使得我們可以直接在一個沒有父類的(也就是不用繼承JFrame)的普通類起步來構建我們的GUI Application。不得不承認,雖然現在IDEA對於Swing的“所見即所得”的拖拉控件可視化做的沒有NetBeans好,雖然我們可以直接在NetBeans上直接拖拉控件然后點擊相應控件來添加我們的邏輯,但是對於真正的開發人員來說卻有一個現象,他們很少使用“拖拉拽”這種“傻瓜式的”添加控件的方式,他們大多都是通過Swing中的布局管理器來組織他們所添加的控件,並調用Swing中布局管理器的相應API和控件本身的API來控制控件的布局。


​ 有過Android開發經驗的朋友應該知道,為什么Android的AbsoluteLayout(絕對布局)已經被人拋棄,早已過時。因為這種布局管理器根本沒有提供什么“布局管理”的支持,它其實就像Swing中你為某個Container調用“setLayout(null);”一樣,它需要我們通過X、Y坐標來控制控件的擺放位置。也許我們在模擬機上試出了漂亮的界面,到了真機測試才發現“這是什么啊”!運行Android應用的手機往往千差萬別,因此屏幕分辨率、屏幕大小一般都存在較大差異,使用絕對布局很難兼顧不同屏幕分辨率、大小的問題。由此我們也不難解釋上面的現象了。


​ 之所以使用IDEA來開發Swing, 還有一個重要的原因,就是所需代碼量極少!你可以先使用NetBeans編寫一個帶有一個按鈕的簡單的GUI界面(不管你使用“拖拉拽”還是純粹手工編寫,當然“脫拉拽”會生成“一大坨”額外的布局代碼,看着很難受),然后看看我下面使用IDEA編寫的代碼:

/* * @program: MFQ * @description: * @author: WuchangI * @create: 2018-05-24-08-48 **/

import javax.swing.*;
public class Testing {
    private JButton button;
    private JPanel panel;
    public static void main(String[] args)
    {
        JFrame frame = new JFrame("Testing");
        frame.setContentPane(new Testing().panel);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(1000, 800);
        frame.setVisible(true);
    }
}

好處很明顯,也不用多講。


​ 除此之外,IDEA的.form文件幫我們封裝了Swing控件的一些布局信息(自動生成的),有點像Android的.xml布局文件,不像NetBeans那樣將業務邏輯和布局顯示都混在一起(有點out了),體現了MVC的應用,這樣有利於專注我們業務邏輯的實現。

一、效果預覽

前面講了那么多廢話,馬上使用IDEA來實現這個MFQ(multi-level feedback queues),這里先看看效果:

1



二、多級反饋隊列調度算法簡介

無論是在批處理系統還是分時系統中,用戶進程數一般都多於處理機數, 這將導致它們互相爭奪處理機。另外,系統進程也同樣需要使用處理機。這就要求進程調度程序按一定的策略,動態地把處理機分配給處於就緒隊列中的某一個進程,以使之執行。因此,我們需要用到一些調度方式來解決進程互相爭奪資源,使得每個進程都很好的使用的處理機。


多級反饋隊列調度算法 就是一種CPU處理機調度算法,是目前公認的較好的一種進程調度算法,它能較好的滿足各類進程的需要,UNIX操作系統采取的便是這種調度算法。多級反饋隊列調度算法既能使高優先級的作業得到響應又能使短作業(進程)迅速完成。具體實現如下:

  1. 應設置多個就緒隊列,並為各個隊列賦予不同的優先級。

    第一個隊列的優先級最高,第二個隊列次之,其余各隊列的優先權逐個降低。該算法賦予各個隊列中進程執行時間片的大小也各不相同,在優先權愈高的隊列中,為每個進程所規定的執行時間片就愈小。

  2. 當一個新進程進入內存后,首先將它放入第一隊列的末尾,按FCFS原則排隊等待調度。當輪到該進程執行時,如它能在該時間片內完成,便可准備撤離系統;如果它在一個時間片結束時尚未完成,調度程序便將該進程轉入第二隊列的末尾,再同樣地按FCFS原則等待調度執行;如果它在第二隊列中運行一個時間片后仍未完成,再依次將它放入第三隊列,……,如此下去,當一個長作業(進程)從第一隊列依次降到第n隊列后,在第n隊列中便采取按時間片輪轉的方式運行。

  3. 僅當第一隊列空閑時,調度程序才調度第二隊列中的進程運行; 僅當第1~(i-1) 隊列均空時,才會調度第i隊列中的進程運行。如果處理機正在第i隊列中為某進程服務時,又有新進程進入優先權較高的隊列(第1~(i-1)中的任何一個隊列),則此時新進程將搶占正在運行進程的處理機,即由調度程序把正在運行的進程放回到第i隊列的末尾,把處理機分配給新到的高優先權進程。



三、需求分析和設計

采用多級反饋隊列調度算法進行進程調度的模擬。

  • 每個進程對應一個 PCB。在 PCB 中包括進程標識符 pid、進程的狀態標識 status、進程優先級 priority、表示進程生命周期的數據項 life(在實際系統中不包括該項)。
  • 創建進程時即創建一個 PCB,各個進程的 pid 都是唯一的,pid 是在 1 到 100 范圍內的一個整數。
  • 可以創建一個下標為 1 到 100 的布爾數組, “假”表示下標對應的進程標識號是空閑的,“真”表示下標對應的進程標識號已分配給某個進程。
  • 進程狀態 status 的取值為“就緒 ready”或“運行 run”,剛創建時,狀態為“ready”。被進程調度程序選中后變為“run”。
  • 進程優先級 priority 是 0(最低) 到 49(最高) 范圍內的一個隨機整數。
  • 進程生命周期 life 是 1 到 5 范圍內的一個隨機整數。
  • 初始化時,創建 50 個就緒隊列,各就緒隊列的進程優先級 priority 分別是 0 到 49。
  • 為了模擬用戶動態提交任務的過程,要求動態創建進程。進入進程調度循環后,每次按 ctrl+f 即動態創建一個進程,然后將該 PCB 插入就緒隊列中。
  • 在進程調度循環中,每次選擇優先級大的就緒進程來執行。將其狀態從就緒變為運行,通過延時一段時間來模擬該進程執行一個時間片 的過程,然后優先級減半,生命周期減一。
  • 如果將該運行進程的生命周期不為 0,則重新把它變為就緒狀態,插入就緒隊列中;否則該進程執行完成,撤消其 PCB。以上為一次進程調度循環。
  • 設計圖形用戶界面 GUI,在窗口中顯示該進程和其他所有進程的 PCB 內容。



四、代碼實現

1. 進程控制塊類

Code:

PCB.java

package com.wuchangi;

/* * @program: MFQ * @description: PCB * @author: WuchangI * @create: 2018-05-20-22-04 **/

//進程控制塊類
public class PCB {
    //進程標識符
    private int pid;

    //進程狀態標識
    private String status;

    //進程優先級
    private int priority;

    //進程生命周期
    private int life;

    public PCB()
    {
    }

    public PCB(int pid, String status, int priority, int life)
    {
        this.pid = pid;
        this.status = status;
        this.priority = priority;
        this.life = life;
    }

    public int getPid()
    {
        return pid;
    }

    public void setPid(int pid)
    {
        this.pid = pid;
    }

    public String getStatus()
    {
        return status;
    }

    public void setStatus(String status)
    {
        this.status = status;
    }

    public int getPriority()
    {
        return priority;
    }

    public void setPriority(int priority)
    {
        this.priority = priority;
    }

    public int getLife()
    {
        return life;
    }

    public void setLife(int life)
    {
        this.life = life;
    }
}


2. 控制塊隊列類

Code:

PCBsQueue.java

package com.wuchangi;

/* * @program: MFQ * @description: PCBsQueue * @author: WuchangI * @create: 2018-05-23-13-49 **/

import java.util.LinkedList;

//控制塊隊列類
class PCBsQueue
{
    //隊列優先級
    private int priority;
    private LinkedList<PCB> queue = new LinkedList<PCB>();


    public PCBsQueue(int priority)
    {
        this.priority = priority;
    }

    public int getPriority()
    {
        return priority;
    }

    public void setPriority(int priority)
    {
        this.priority = priority;
    }

    public LinkedList<PCB> getQueue()
    {
        return queue;
    }

    public void setQueue(LinkedList<PCB> queue)
    {
        this.queue = queue;
    }
}


3. 多級反饋隊列進程調度模擬類

Code:

MFQSimulation.java


package com.wuchangi;

/* * @program: MFQ * @description: MFQSimulation * @author: WuchangI * @create: 2018-05-20-22-04 **/


import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.util.Arrays;
import java.util.LinkedList;


public class MFQSimulation {
    private static JFrame frame = new JFrame("進程調度模擬(多級反饋隊列)");
    private static JPanel panel = new JPanel();
    private static JScrollPane scrollPane = new JScrollPane(panel, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);

    //菜單組件
    private static JMenuBar menuBar = new JMenuBar();
    private static JMenu processSettingsMenu = new JMenu("Process Settings");
    private static JMenuItem createProcessItem = new JMenuItem("Create A Process");
    private static JMenuItem startMFQItem = new JMenuItem("Start Scheduling");
    private static JMenuItem stopMFQItem = new JMenuItem("Stop Scheduling");
    private static JMenuItem setTimeSliceItem = new JMenuItem("Set Time Slice");
    private static JMenuItem exitSystemItem = new JMenuItem("Exit");
    private static JMenu helpMenu = new JMenu("Help");
    private static JMenuItem aboutItem = new JMenuItem("About");

    //設置優先級最高(即49)的隊列的時間片大小默認值(單位:秒)
    public static double timeSlice = 0.5;

    //設置每個隊列對應的時間片大小
    public static double PCBsQueuesTimeSlice[] = new double[50];

    //多級反饋隊列
    public static PCBsQueue[] PCBsQueues = new PCBsQueue[50];

    //記錄已經使用的pid
    public static int[] pidsUsed = new int[101];

    //當前內存中的進程數
    public static int currentPCBsNum = 0;

    //內存中能夠容納的最大進程數(這里取決於可分配的pid的個數)
    public static final int PCBS_MAX_NUM = 100;

    //是否停止調度
    public static boolean isStopScheduling;

    //很短的main函數
    public static void main(String[] args)
    {
        new MFQSimulation().initWindow();
    }



    //執行窗口初始化
    public void initWindow()
    {
        //設置窗口風格為Windows風格
        setWindowsStyle();

        //創建菜單欄
        processSettingsMenu.add(createProcessItem);
        processSettingsMenu.addSeparator();
        processSettingsMenu.add(startMFQItem);
        processSettingsMenu.addSeparator();
        processSettingsMenu.add(stopMFQItem);
        processSettingsMenu.addSeparator();
        processSettingsMenu.add(setTimeSliceItem);
        processSettingsMenu.addSeparator();
        processSettingsMenu.add(exitSystemItem);

        helpMenu.add(aboutItem);

        menuBar.add(processSettingsMenu);
        menuBar.add(helpMenu);

        frame.setJMenuBar(menuBar);

        initMemory();

        panel.setBorder(BorderFactory.createLineBorder(Color.BLACK));

        frame.setContentPane(scrollPane);
        frame.setSize(800, 700);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);


        //為控件綁定監聽器
        setComponentsListeners();
    }

    //設置Swing的控件顯示風格為Windows風格
    public static void setWindowsStyle()
    {
        try
        {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        }
        catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e)
        {
            e.printStackTrace();
        }

    }

    //初始化相關內存參數
    public static void initMemory()
    {
        currentPCBsNum = 0;

        Arrays.fill(pidsUsed, 1, 101, 0);

        for(int i = 0; i < PCBsQueues.length; i++)
        {
            PCBsQueues[i] = new PCBsQueue(i);
        }

        for(int i = PCBsQueuesTimeSlice.length - 1; i >= 0; i--)
        {
            //隊列優先級每降一級,時間片增加0.1秒
            PCBsQueuesTimeSlice[i] = timeSlice;
            timeSlice += 0.1;
        }
    }

    //給窗口中所有控件綁定監聽器
    public static void setComponentsListeners()
    {
        createProcessItem.setAccelerator(KeyStroke.getKeyStroke('F', InputEvent.CTRL_MASK));
        createProcessItem.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                createProcess();
            }
        });


        startMFQItem.setAccelerator(KeyStroke.getKeyStroke('S', InputEvent.CTRL_MASK));
        startMFQItem.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                startMFQSimulation();
            }
        });

        stopMFQItem.setAccelerator(KeyStroke.getKeyStroke('P', InputEvent.CTRL_MASK));
        stopMFQItem.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                stopMFQSimulation();
            }
        });

        setTimeSliceItem.setAccelerator(KeyStroke.getKeyStroke('T', InputEvent.CTRL_MASK));
        setTimeSliceItem.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                setTimeSlice();
            }
        });


        exitSystemItem.setAccelerator(KeyStroke.getKeyStroke('E', InputEvent.CTRL_MASK));
        exitSystemItem.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                System.exit(0);
            }
        });

        aboutItem.setAccelerator(KeyStroke.getKeyStroke('A', InputEvent.CTRL_MASK));
        aboutItem.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                JOptionPane.showMessageDialog(frame, "Multilevel feedback queue simulation application (1.0 version)\n\nCopyright © 2018, 余梓權, All Rights Reserved.");
            }
        });

    }

    //創建新進程
    public static void createProcess()
    {
        if(currentPCBsNum == PCBS_MAX_NUM)
        {
            JOptionPane.showMessageDialog(frame,"The current memory space is full and cannot create a new process!");
        }
        else
        {
            currentPCBsNum++;

            int randomPid = 1 + (int)(Math.random() * ((100 - 1) + 1));

            while(pidsUsed[randomPid] == 1)
            {
                randomPid = 1 + (int)(Math.random() * ((100 - 1) + 1));
            }

            pidsUsed[randomPid] = 1;

            int randomPriority = 0 + (int)(Math.random() * ((49 - 0) + 1));
            int randomLife = 1 + (int)(Math.random() * ((5 - 1) + 1));

            PCB pcb = new PCB(randomPid, "Ready", randomPriority, randomLife);

            LinkedList<PCB> queue = PCBsQueues[randomPriority].getQueue();
            queue.offer(pcb);
            PCBsQueues[randomPriority].setQueue(queue);

            showPCBQueues(PCBsQueues);
        }
    }

    //開始調度
    public static void startMFQSimulation()
    {
        isStopScheduling = false;

        //更新界面操作必須借助多線程來實現
        new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                //當前內存中還留有進程未執行
                while(currentPCBsNum!=0 && !isStopScheduling)
                {
                    for(int i = PCBsQueues.length - 1; i >= 0; i--)
                    {
                        LinkedList<PCB> queue = PCBsQueues[i].getQueue();

                        if (queue.size() > 0)
                        {
                            //讀取該隊列首個PCB
                            PCB pcb = queue.element();
                            pcb.setStatus("Running");
                            showPCBQueues(PCBsQueues);

                            int pid = pcb.getPid();
                            int priority = pcb.getPriority();
                            int life = pcb.getLife();
                            priority = priority / 2;
                            life = life - 1;

                            //通過延時一個時間片來模擬該進程的執行
                            try
                            {
                                Thread.sleep((int)(PCBsQueuesTimeSlice[priority] * 1000));
                            }
                            catch (InterruptedException e)
                            {
                                e.printStackTrace();
                            }

                            //若該進程執行完成
                            if(life == 0)
                            {
                                //移除該隊列的首個PCB
                                queue.poll();
                                pidsUsed[pid] = 0;
                                currentPCBsNum--;
                            }
                            //若該進程還未執行完成,則改變其PCB的相關參數,並插入其優先級所對應的隊列尾部
                            else
                            {
                                //移除該隊列的首個PCB
                                queue.poll();

                                pcb.setPriority(priority);
                                pcb.setLife(life);
                                pcb.setStatus("Ready");
                                LinkedList<PCB> nextQueue = PCBsQueues[priority].getQueue();
                                nextQueue.offer(pcb);
                                PCBsQueues[priority].setQueue(nextQueue);
                            }

                            break;
                        }
                    }
                }

                initMemory();
                showPCBQueues(PCBsQueues);
                //所有進程均執行完成,進程調度完成
                JOptionPane.showMessageDialog(frame, "Process scheduling over!");
            }
        }).start();

    }

    //強制結束進程調度
    public static void stopMFQSimulation()
    {
        isStopScheduling = true;
        initMemory();
    }

    //設置時間片大小
    public static void setTimeSlice()
    {
        String inputMsg = JOptionPane.showInputDialog(frame, "Please input your time slice(seconds):", 0.5);

        double timeSliceInput = Double.parseDouble(inputMsg);

        while(timeSliceInput <= 0)
        {
            JOptionPane.showMessageDialog(frame, "Time Slice is illegal, Please set time slice again!");
            inputMsg = JOptionPane.showInputDialog(frame, "Please input your time slice(seconds):", "Set Time Slice", JOptionPane.PLAIN_MESSAGE);
            timeSliceInput = Integer.parseInt(inputMsg);
        }

        timeSlice = timeSliceInput;
    }

    //顯示內存中的多級反饋隊列
    public static void showPCBQueues(PCBsQueue[] PCBsQueues)
    {
        int queueLocationY = 0;
        JPanel queuesPanel = new JPanel();

        for(int i = PCBsQueues.length - 1; i >= 0; i--)
        {
            LinkedList<PCB> queue = PCBsQueues[i].getQueue();

            if (queue.size() > 0)
            {
                //創建一個PCB隊列
                JPanel PCBsQueue = new JPanel();
                // PCBsQueue.setBorder(BorderFactory.createLineBorder(Color.BLACK));
                PCBsQueue.setLayout(new FlowLayout(FlowLayout.LEFT));
                PCBsQueue.setBounds(0, queueLocationY, 800, 700);

                queueLocationY += 50;

                //創建隊列前面的優先級提示塊
                JLabel PCBsQueuePriorityLabel = new JLabel("Priority of queue: " + String.valueOf(i));
                PCBsQueuePriorityLabel.setOpaque(true);
                PCBsQueuePriorityLabel.setBackground(Color.RED);
                PCBsQueuePriorityLabel.setForeground(Color.YELLOW);

                JPanel PCBsQueuePriorityBlock = new JPanel();
                PCBsQueuePriorityBlock.add(PCBsQueuePriorityLabel);

                PCBsQueue.add(PCBsQueuePriorityBlock);

                for (PCB pcb : queue)
                {

                    //JLabel默認情況下是透明的所以直接設置背景顏色是無法顯示的,必須將其設置為不透明才能顯示背景

                    //設置pid標簽
                    JLabel pidLabel = new JLabel("Pid: " + String.valueOf(pcb.getPid()));
                    pidLabel.setOpaque(true);
                    pidLabel.setBackground(Color.GREEN);
                    pidLabel.setForeground(Color.RED);
                    pidLabel.setBorder(BorderFactory.createLineBorder(Color.BLACK));

                    //設置status標簽
                    JLabel statusLabel = new JLabel("Status: " + pcb.getStatus());
                    statusLabel.setOpaque(true);
                    statusLabel.setBackground(Color.GREEN);
                    statusLabel.setForeground(Color.RED);
                    statusLabel.setBorder(BorderFactory.createLineBorder(Color.BLACK));

                    //設置priority標簽
                    JLabel priorityLabel = new JLabel("Priority: " + String.valueOf(pcb.getPriority()));
                    priorityLabel.setOpaque(true);
                    priorityLabel.setBackground(Color.GREEN);
                    priorityLabel.setForeground(Color.RED);
                    priorityLabel.setBorder(BorderFactory.createLineBorder(Color.BLACK));

                    //設置life標簽
                    JLabel lifeLabel = new JLabel("Life: " + String.valueOf(pcb.getLife()));
                    lifeLabel.setOpaque(true);
                    lifeLabel.setBackground(Color.GREEN);
                    lifeLabel.setForeground(Color.RED);
                    lifeLabel.setBorder(BorderFactory.createLineBorder(Color.BLACK));

                    //繪制一個PCB
                    JPanel PCBPanel = new JPanel();
                    PCBPanel.setBorder(BorderFactory.createLineBorder(Color.BLACK));
                    PCBPanel.setBackground(Color.BLUE);
                    PCBPanel.add(pidLabel);
                    PCBPanel.add(statusLabel);
                    PCBPanel.add(priorityLabel);
                    PCBPanel.add(lifeLabel);

                    //將PCB加入隊列
                    PCBsQueue.add(new DrawLinePanel());
                    PCBsQueue.add(PCBPanel);
                }

                queuesPanel.add(PCBsQueue);
            }
        }


        //設置queuesPanel中的所有PCB隊列(PCBsQueue組件)按垂直方向排列
        BoxLayout boxLayout = new BoxLayout(queuesPanel, BoxLayout.Y_AXIS);
        queuesPanel.setLayout(boxLayout);

        queuesPanel.setSize(800, 700);

        panel.setLayout(new FlowLayout(FlowLayout.LEFT));
        panel.removeAll();
        panel.add(queuesPanel);
        panel.updateUI();
        panel.repaint();
    }

}



//繪制直線類
class DrawLinePanel extends JPanel
{
    @Override
    protected void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        g.drawLine(0, this.getSize().height / 2, this.getSize().width, this.getSize().height/2);

    }

}



五、相關說明

鑒於上述代碼都添加了必要的注釋,故這里也不細究代碼的細節問題。

  • 此次進程調度的模擬實現的關鍵在於MFQSimulation這個類,GUI的實現關鍵在於該類中的showPCBQueues靜態方法。
  • 實現實時更新界面不能使用主線程(默認當前線程即為主線程)來更新,而應借助多線程的技術來實現更新操作。
  • 鑒於Swing默認的界面顯示風格比較丑,所以使用了Windows風格來顯示。
  • 設置組件垂直排列,可借助BoxLayout。
  • 關於向Panel中動態添加組件並顯示的問題:

eg:


JPanel panel = new JPanel();
......

監聽器方法
{
    panel.removeAll();

    JButton button = new JButton("Hello");
    panel.add(button);

    panel.updateUI();
    panel.repaint();
}

當觸發該控件,以上代碼成功添加並在JPanel中顯示了一個按鈕。


但是,如果向JPanel中添加“沒有布局”的JPanel(這里假設為panel已經被你設置panel.setLayout(null),因為你想要使用絕對布局方式),則最終只是顯示一個小黑點,沒有成功顯示你所添加的panel。


綜上,如果要為JPanel添加JPanel,被添加的JPanel一定要帶有相應的布局管理器!


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com