linux进程调度中的多级反馈队列

264 阅读6分钟

java重写linux第一章

多级反馈队列 (Multilevel Feedback Queue)

多级反馈队列调度算法,顾名思义,就是利用 多个就绪队列 来实现 CPU 调度。它综合考虑了先来先服务、短作业优先、优先级调度等算法的优点,并致力于克服它们的缺点,是一种兼顾公平性和效率的调度算法。

1. 队列结构:

  • 系统中存在多个就绪队列,每个队列对应不同的优先级或时间片大小。
  • 高优先级队列通常拥有较短的时间片,而低优先级队列拥有较长的时间片。

2. 进程调度:

  • 新进程进入系统后,会被放入最高优先级队列的末尾。
  • CPU 总是先从最高优先级队列头部选择进程执行。
  • 如果进程在其时间片内完成,则退出系统;否则,它会被降级到下一级优先级队列的末尾。
  • 低优先级队列中的进程只有在高优先级队列为空时才有机会被调度执行。

3. 队列之间的反馈:

  • 多级反馈队列的关键在于队列之间的 反馈机制
  • 如果一个进程在低优先级队列中等待时间过长,它可能会被提升到更高优先级队列中,以避免 “饥饿” 现象。

多级反馈队列的优点:

  • 兼顾短作业和长作业: 短作业可以在高优先级队列中快速完成,而长作业则可以在低优先级队列中逐渐执行。
  • 响应时间快: 高优先级队列的短时间片可以保证交互性任务 (例如用户输入) 能够得到快速响应。
  • 灵活可调: 可以通过调整队列数量、优先级、时间片大小等参数来适应不同的应用场景。

多级反馈队列的缺点:

  • 实现较为复杂: 需要维护多个队列和复杂的调度逻辑。
  • 参数设置困难: 找到最佳的参数配置需要经验和对系统负载的了解。

为了构建一个更加复杂的多级反馈队列(Multi-Level Feedback Queue, MLFQ)调度器,我们可以引入一些更高级的调度机制。例如:

  1. 不同的时间片策略:每个队列有不同的时间片长度,且不一定是简单的倍数关系。
  2. 老化机制:防止某个进程在低优先级队列中长期得不到执行(避免饥饿问题)。
  3. I/O阻塞与恢复:模拟进程因I/O操作阻塞,并在I/O完成后恢复到适当的优先级队列中。
import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;

class Process {
    String name;
    int burstTime; // 该进程需要的总 CPU 时间
    int remainingTime; // 剩余执行时间
    int currentQueue; // 当前所在的优先级队列
    int waitTime; // 等待时间,用于老化机制
    boolean ioBlocked; // 是否进入 I/O 阻塞状态

    public Process(String name, int burstTime) {
        this.name = name;
        this.burstTime = burstTime;
        this.remainingTime = burstTime;
        this.currentQueue = 0; // 初始在最高优先级队列
        this.waitTime = 0; // 等待时间初始为 0
        this.ioBlocked = false; // 初始时未阻塞
    }

    public boolean isFinished() {
        return remainingTime <= 0;
    }

    public void blockForIO() {
        ioBlocked = true;
        System.out.println("Process " + name + " is blocked for I/O.");
    }

    public void unblockFromIO() {
        ioBlocked = false;
        System.out.println("Process " + name + " has returned from I/O.");
    }
}

public class AdvancedMLFQScheduler {
    private int numQueues; // 队列数量
    private int[] timeQuanta; // 每个队列的时间片
    private Queue<Process>[] queues; // 队列数组
    private int maxWaitTime; // 老化机制中的最大等待时间

    @SuppressWarnings("unchecked")
    public AdvancedMLFQScheduler(int numQueues, int[] timeQuanta, int maxWaitTime) {
        this.numQueues = numQueues;
        this.timeQuanta = timeQuanta;
        this.maxWaitTime = maxWaitTime;
        queues = new LinkedList[numQueues];
        for (int i = 0; i < numQueues; i++) {
            queues[i] = new LinkedList<>();
        }
    }

    // 添加进程到最高优先级队列
    public void addProcess(Process process) {
        queues[0].add(process);
    }

    // 模拟调度
    public void schedule() {
        Random random = new Random();

        while (!allQueuesEmpty()) {
            for (int i = 0; i < numQueues; i++) {
                Queue<Process> queue = queues[i];
                int quantum = timeQuanta[i];

                while (!queue.isEmpty()) {
                    Process process = queue.poll();

                    // 如果进程被I/O阻塞,跳过它
                    if (process.ioBlocked) {
                        continue;
                    }

                    System.out.println("Executing process: " + process.name + " from queue " + i);

                    // 模拟进程可能触发 I/O 阻塞
                    if (random.nextInt(10) < 2) { // 20% 几率进入 I/O
                        process.blockForIO();
                        continue;
                    }

                    // 该队列分配的时间片
                    int executionTime = Math.min(quantum, process.remainingTime);
                    process.remainingTime -= executionTime;
                    process.waitTime = 0; // 重置等待时间

                    System.out.println("Process " + process.name + " executed for " + executionTime + " units, remaining time: " + process.remainingTime);

                    if (process.isFinished()) {
                        System.out.println("Process " + process.name + " has finished.");
                    } else {
                        // 如果没完成,降级到下一个优先级队列
                        if (i < numQueues - 1) {
                            process.currentQueue = i + 1;
                            queues[i + 1].add(process);
                            System.out.println("Process " + process.name + " moved to queue " + (i + 1));
                        } else {
                            // 如果已经在最低优先级队列,继续留在该队列
                            queues[i].add(process);
                            System.out.println("Process " + process.name + " stays in queue " + i);
                        }
                    }

                    // 模拟 I/O 完成
                    if (!process.ioBlocked && random.nextInt(10) < 1) { // 10% 几率解除 I/O 阻塞
                        process.unblockFromIO();
                    }

                    // 老化机制:检查所有低优先级队列中的进程
                    applyAging(i);
                }
            }
        }
    }

    // 老化机制:如果进程等待时间过长,提升其优先级
    private void applyAging(int currentQueue) {
        for (int i = currentQueue + 1; i < numQueues; i++) {
            Queue<Process> queue = queues[i];
            for (Process process : queue) {
                process.waitTime++;
                if (process.waitTime > maxWaitTime) {
                    queue.remove(process);
                    process.currentQueue = i - 1;
                    queues[i - 1].add(process);
                    System.out.println("Process " + process.name + " has been aged and moved to queue " + (i - 1));
                }
            }
        }
    }

    // 检查所有队列是否为空
    private boolean allQueuesEmpty() {
        for (int i = 0; i < numQueues; i++) {
            if (!queues[i].isEmpty()) {
                return false;
            }
        }
        return true;
    }

    public static void main(String[] args) {
        // 定义四个优先级队列,时间片分别为 2、4、8、16,最大等待时间为 10
        int[] timeQuanta = {2, 4, 8, 16};
        AdvancedMLFQScheduler scheduler = new AdvancedMLFQScheduler(4, timeQuanta, 10);

        // 创建一些进程
        Process p1 = new Process("P1", 20);
        Process p2 = new Process("P2", 10);
        Process p3 = new Process("P3", 15);
        Process p4 = new Process("P4", 25);

        // 添加进程到调度器
        scheduler.addProcess(p1);
        scheduler.addProcess(p2);
        scheduler.addProcess(p3);
        scheduler.addProcess(p4);

        // 开始调度
        scheduler.schedule();
    }
}

输出示例

Executing process: P1 from queue 0
Process P1 executed for 2 units, remaining time: 18
Process P1 moved to queue 1
Executing process: P2 from queue 0
Process P2 executed for 2 units, remaining time: 8
Process P2 moved to queue 1
Executing process: P3 from queue 0
Process P3 executed for 2 units, remaining time: 13
Process P3 moved to queue 1
Executing process: P4 from queue 0
Process P4 executed for 2 units, remaining time: 23
Process P4 moved to queue 1
Executing process: P1 from queue 1
Process P1 executed for 4 units, remaining time: 14
Process P1 moved to queue 2
Executing process: P2 from queue 1
Process P2 executed for 4 units, remaining time: 4
Process P2 moved to queue 2
...

代码解释

  1. I/O 阻塞与恢复

    • 在调度过程中,进程有一定概率(20%)进入 I/O 阻塞状态。一旦阻塞,进程会在后续调度中跳过,直到模拟的 I/O 操作完成(10% 概率解除阻塞)。
    • 解除阻塞后,进程会继续从其之前所在的队列执行。
  2. 老化机制(Aging)

    • 如果某个进程在低优先级队列中等待时间过长(超过 maxWaitTime),则其优先级会自动提升到上一个较高优先级的队列。这是为了避免进程长期处于低优先级而得不到执行,解决“饥饿”问题。
  3. 不同的时间片策略

    • 每个优先级队列有不同的时间片,随着优先级的降低,时间片逐渐增大。这个策略使得短作业可以在高优先级队列中快速完成,而长作业则逐渐向低优先级队列迁移,获得更长的时间片。
  4. 调度循环

    • 调度器从最高优先级队列开始,依次调度各个队列中的进程