进程调度算法对比

进程调度算法对比:在 Python 中实现最短作业优先(SJF)和时间片轮转(RR)进程调度算法,设置多个进程的相关参数(如到达时间、执行时间、时间片大小等),分别模拟这两种算法的调度过程,计算并比较它们的性能指标(如平均等待时间、平均周转时间等),分析不同算法在不同场景下的优缺点。

import matplotlib.pyplot as plt
import numpy as np
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import tkinter as tk
from tkinter import ttk, scrolledtext
from matplotlib.font_manager import FontProperties

# 设置中文字体
try:
    font = FontProperties(fname=r"C:\Windows\Fonts\simhei.ttf")  # Windows系统默认黑体
except:
    try:
        font = FontProperties(fname=r"/System/Library/Fonts/PingFang.ttc")  # macOS系统
    except:
        try:
            font = FontProperties(fname=r"/usr/share/fonts/truetype/wqy/wqy-microhei.ttc")  # Linux系统
        except:
            font = None  # 如果找不到中文字体,使用默认字体


class Process:
    def __init__(self, pid, arrival_time, burst_time):
        self.pid = pid  # 进程ID
        self.arrival_time = arrival_time  # 到达时间
        self.burst_time = burst_time  # 执行时间
        self.remaining_time = burst_time  # 剩余执行时间
        self.start_time = None  # 首次执行时间
        self.completion_time = None  # 完成时间
        self.waiting_time = 0  # 等待时间
        self.turnaround_time = 0  # 周转时间


def sjf_scheduling(processes):
    """实现最短作业优先(SJF)调度算法"""
    # 按到达时间排序
    processes.sort(key=lambda x: x.arrival_time)
    n = len(processes)
    current_time = 0
    completed = 0
    ready_queue = []
    scheduled_order = []  # 记录调度顺序
    time_points = []  # 记录时间点

    while completed != n:
        # 将已到达的进程加入就绪队列
        for process in processes:
            if process.arrival_time <= current_time and process not in ready_queue and process.completion_time is None:
                ready_queue.append(process)

        # 按剩余执行时间排序(SJF的核心)
        ready_queue.sort(key=lambda x: x.remaining_time)

        if not ready_queue:
            current_time += 1
            continue

        # 获取当前要执行的进程
        current_process = ready_queue.pop(0)

        # 记录调度信息
        if current_process.start_time is None:
            current_process.start_time = current_time

        # 执行该进程直到完成
        current_time += current_process.remaining_time
        current_process.remaining_time = 0
        current_process.completion_time = current_time
        current_process.turnaround_time = current_process.completion_time - current_process.arrival_time
        current_process.waiting_time = current_process.turnaround_time - current_process.burst_time

        # 记录调度顺序和时间点
        scheduled_order.append(current_process.pid)
        time_points.append(current_time)

        completed += 1

    # 计算平均指标
    avg_waiting_time = sum(p.waiting_time for p in processes) / n
    avg_turnaround_time = sum(p.turnaround_time for p in processes) / n

    return processes, avg_waiting_time, avg_turnaround_time, scheduled_order, time_points


def rr_scheduling(processes, time_quantum):
    """实现时间片轮转(RR)调度算法"""
    # 按到达时间排序
    processes = sorted(processes, key=lambda x: x.arrival_time)
    n = len(processes)
    current_time = 0
    completed = 0
    ready_queue = []
    temp_processes = [Process(p.pid, p.arrival_time, p.burst_time) for p in processes]
    scheduled_order = []  # 记录调度顺序
    time_points = []  # 记录时间点
    time_quantum_points = []  # 记录时间片切换点

    while completed != n:
        # 将已到达的进程加入就绪队列
        for process in temp_processes:
            if process.arrival_time <= current_time and process not in ready_queue and process.remaining_time > 0:
                ready_queue.append(process)

        if not ready_queue:
            current_time += 1
            continue

        # 获取当前要执行的进程(FIFO顺序)
        current_process = ready_queue.pop(0)

        # 记录调度信息
        if current_process.start_time is None:
            current_process.start_time = current_time

        # 执行一个时间片或直到进程完成
        execution_time = min(time_quantum, current_process.remaining_time)
        current_process.remaining_time -= execution_time
        current_time += execution_time

        # 记录调度顺序和时间点
        scheduled_order.append(current_process.pid)
        time_points.append(current_time)
        time_quantum_points.append(current_time)

        # 检查进程是否完成
        if current_process.remaining_time == 0:
            current_process.completion_time = current_time
            current_process.turnaround_time = current_process.completion_time - current_process.arrival_time
            current_process.waiting_time = current_process.turnaround_time - current_process.burst_time
            completed += 1
        else:
            # 未完成则重新加入就绪队列
            current_process.arrival_time = current_time  # 更新到达时间为当前时间
            ready_queue.append(current_process)

    # 计算平均指标
    avg_waiting_time = sum(p.waiting_time for p in temp_processes) / n
    avg_turnaround_time = sum(p.turnaround_time for p in temp_processes) / n

    return temp_processes, avg_waiting_time, avg_turnaround_time, scheduled_order, time_points, time_quantum_points


class ProcessSchedulingApp:
    def __init__(self, root):
        self.root = root
        self.root.title("进程调度算法可视化")
        self.root.geometry("900x700")

        # 设置中文字体
        self.font = ('SimHei', 10) if font else ('Arial', 10)

        # 初始化时间片大小
        self.time_quantum = 2  # 默认时间片大小

        # 初始化示例进程
        self.processes = [
            Process(1, 0, 5),
            Process(2, 1, 3),
            Process(3, 2, 8),
            Process(4, 3, 6)
        ]

        # 创建主框架
        self.main_frame = ttk.Frame(root, padding="10")
        self.main_frame.pack(fill=tk.BOTH, expand=True)

        # 标题
        ttk.Label(self.main_frame, text="进程调度算法对比", font=('SimHei', 16, 'bold')).pack(pady=10)

        # 进程参数设置
        self.setup_process_frame()

        # 按钮框架
        btn_frame = ttk.Frame(self.main_frame)
        btn_frame.pack(fill=tk.X, pady=10)

        ttk.Button(btn_frame, text="运行模拟", command=self.run_simulation).pack(side=tk.LEFT, padx=5)
        ttk.Button(btn_frame, text="显示算法分析", command=self.show_algorithm_analysis).pack(side=tk.LEFT, padx=5)

        # 结果框架
        self.result_frame = ttk.Frame(self.main_frame)
        self.result_frame.pack(fill=tk.BOTH, expand=True, pady=10)

        # 初始化结果文本区域
        self.result_text = scrolledtext.ScrolledText(self.result_frame, wrap=tk.WORD, height=15)
        self.result_text.pack(fill=tk.BOTH, expand=True)

        # 初始化图表
        self.fig, (self.ax1, self.ax2) = plt.subplots(2, 1, figsize=(10, 8))
        self.canvas = FigureCanvasTkAgg(self.fig, master=self.result_frame)
        self.canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)

    def setup_process_frame(self):
        """设置进程参数输入框架"""
        process_frame = ttk.LabelFrame(self.main_frame, text="进程参数设置", padding="10")
        process_frame.pack(fill=tk.X, pady=10)

        # 时间片设置
        ttk.Label(process_frame, text="时间片大小:", font=self.font).grid(row=0, column=0, sticky=tk.W, pady=5)
        self.time_quantum_var = tk.IntVar(value=self.time_quantum)
        ttk.Entry(process_frame, textvariable=self.time_quantum_var, width=5).grid(row=0, column=1, sticky=tk.W, pady=5)

        # 进程列表
        ttk.Label(process_frame, text="进程列表:", font=self.font).grid(row=1, column=0, sticky=tk.W, pady=5)

        self.process_entries = []
        for i, p in enumerate(self.processes):
            frame = ttk.Frame(process_frame)
            frame.grid(row=i + 2, column=0, columnspan=3, sticky=tk.W, pady=2)

            ttk.Label(frame, text=f"进程 {i + 1}:", width=8, font=self.font).pack(side=tk.LEFT, padx=2)

            arrival_var = tk.IntVar(value=p.arrival_time)
            ttk.Label(frame, text="到达时间:", width=8, font=self.font).pack(side=tk.LEFT, padx=2)
            ttk.Entry(frame, textvariable=arrival_var, width=5).pack(side=tk.LEFT, padx=2)

            burst_var = tk.IntVar(value=p.burst_time)
            ttk.Label(frame, text="执行时间:", width=8, font=self.font).pack(side=tk.LEFT, padx=2)
            ttk.Entry(frame, textvariable=burst_var, width=5).pack(side=tk.LEFT, padx=2)

            self.process_entries.append((arrival_var, burst_var))

    def get_processes_from_entries(self):
        """从输入框获取进程参数"""
        processes = []
        for i, (arrival_var, burst_var) in enumerate(self.process_entries):
            pid = i + 1
            arrival_time = arrival_var.get()
            burst_time = burst_var.get()
            processes.append(Process(pid, arrival_time, burst_time))
        return processes

    def run_simulation(self):
        """运行调度模拟并显示结果"""
        # 获取参数
        self.processes = self.get_processes_from_entries()
        self.time_quantum = self.time_quantum_var.get()

        # 复制进程列表以避免修改原始数据
        sjf_processes = [Process(p.pid, p.arrival_time, p.burst_time) for p in self.processes]
        rr_processes = [Process(p.pid, p.arrival_time, p.burst_time) for p in self.processes]

        # 执行SJF调度
        sjf_result, sjf_avg_wait, sjf_avg_turn, sjf_order, sjf_times = sjf_scheduling(sjf_processes)

        # 执行RR调度
        rr_result, rr_avg_wait, rr_avg_turn, rr_order, rr_times, rr_quantum_points = rr_scheduling(rr_processes,
                                                                                                   self.time_quantum)

        # 保存结果供分析使用
        self.sjf_avg_wait = sjf_avg_wait
        self.sjf_avg_turn = sjf_avg_turn
        self.rr_avg_wait = rr_avg_wait
        self.rr_avg_turn = rr_avg_turn

        # 显示结果
        self.result_text.delete(1.0, tk.END)

        self.result_text.insert(tk.END, "\n===== 最短作业优先(SJF)调度 =====\n")
        self.result_text.insert(tk.END, "进程\t到达时间\t执行时间\t完成时间\t周转时间\t等待时间\n")
        for p in sjf_result:
            self.result_text.insert(tk.END,
                                    f"P{p.pid}\t{p.arrival_time}\t\t{p.burst_time}\t\t{p.completion_time}\t\t{p.turnaround_time}\t\t{p.waiting_time}\n")
        self.result_text.insert(tk.END, f"平均等待时间: {sjf_avg_wait:.2f}\n")
        self.result_text.insert(tk.END, f"平均周转时间: {sjf_avg_turn:.2f}\n\n")

        self.result_text.insert(tk.END, "===== 时间片轮转(RR)调度 =====\n")
        self.result_text.insert(tk.END, f"时间片大小: {self.time_quantum}\n")
        self.result_text.insert(tk.END, "进程\t到达时间\t执行时间\t完成时间\t周转时间\t等待时间\n")
        for p in rr_result:
            self.result_text.insert(tk.END,
                                    f"P{p.pid}\t{p.arrival_time}\t\t{p.burst_time}\t\t{p.completion_time}\t\t{p.turnaround_time}\t\t{p.waiting_time}\n")
        self.result_text.insert(tk.END, f"平均等待时间: {rr_avg_wait:.2f}\n")
        self.result_text.insert(tk.END, f"平均周转时间: {rr_avg_turn:.2f}\n")

        # 更新图表
        self.update_charts(sjf_order, sjf_times, rr_order, rr_times, rr_quantum_points,
                           sjf_avg_wait, sjf_avg_turn, rr_avg_wait, rr_avg_turn)

    def update_charts(self, sjf_order, sjf_times, rr_order, rr_times, rr_quantum_points,
                      sjf_avg_wait, sjf_avg_turn, rr_avg_wait, rr_avg_turn):
        """更新图表显示"""
        # 清除现有图表
        self.ax1.clear()
        self.ax2.clear()

        # SJF甘特图
        self.ax1.set_title('最短作业优先(SJF)调度', fontproperties=font)
        self.ax1.set_xlabel('时间', fontproperties=font)
        self.ax1.set_ylabel('进程', fontproperties=font)

        prev_time = 0
        y_pos = 0.5
        colors = plt.cm.tab20(np.linspace(0, 1, len(set(sjf_order))))

        for i, (pid, time) in enumerate(zip(sjf_order, sjf_times)):
            duration = time - prev_time
            color_idx = list(set(sjf_order)).index(pid)
            self.ax1.barh(y_pos, duration, left=prev_time, height=0.6,
                          color=colors[color_idx], edgecolor='black', alpha=0.8)
            self.ax1.text(prev_time + duration / 2, y_pos, f'P{pid}', ha='center', va='center', fontproperties=font)
            prev_time = time

        self.ax1.set_ylim(0, 1)
        self.ax1.set_xlim(0, max(sjf_times))
        self.ax1.set_yticks([])
        self.ax1.grid(axis='x', linestyle='--', alpha=0.7)

        # 添加平均时间信息
        self.ax1.text(0.02, 0.9, f'平均等待时间: {sjf_avg_wait:.2f}', transform=self.ax1.transAxes, fontproperties=font)
        self.ax1.text(0.02, 0.8, f'平均周转时间: {sjf_avg_turn:.2f}', transform=self.ax1.transAxes, fontproperties=font)

        # RR甘特图
        self.ax2.set_title(f'时间片轮转(RR)调度 (时间片={self.time_quantum})', fontproperties=font)
        self.ax2.set_xlabel('时间', fontproperties=font)
        self.ax2.set_ylabel('进程', fontproperties=font)

        prev_time = 0
        y_pos = 0.5

        for i, (pid, time) in enumerate(zip(rr_order, rr_times)):
            duration = time - prev_time
            color_idx = list(set(rr_order)).index(pid)
            self.ax2.barh(y_pos, duration, left=prev_time, height=0.6,
                          color=colors[color_idx], edgecolor='black', alpha=0.8)
            self.ax2.text(prev_time + duration / 2, y_pos, f'P{pid}', ha='center', va='center', fontproperties=font)
            prev_time = time

        # 标记时间片切换点
        for t in rr_quantum_points[:-1]:
            self.ax2.axvline(x=t, color='red', linestyle='--', alpha=0.5)

        self.ax2.set_ylim(0, 1)
        self.ax2.set_xlim(0, max(rr_times))
        self.ax2.set_yticks([])
        self.ax2.grid(axis='x', linestyle='--', alpha=0.7)

        # 添加平均时间信息
        self.ax2.text(0.02, 0.9, f'平均等待时间: {rr_avg_wait:.2f}', transform=self.ax2.transAxes, fontproperties=font)
        self.ax2.text(0.02, 0.8, f'平均周转时间: {rr_avg_turn:.2f}', transform=self.ax2.transAxes, fontproperties=font)

        # 调整布局并绘制
        self.fig.tight_layout()
        self.canvas.draw()

    def show_algorithm_analysis(self):
        """显示算法分析窗口"""
        if not hasattr(self, 'sjf_avg_wait'):
            # 如果还没有运行模拟,先运行模拟
            self.run_simulation()

        analysis_window = tk.Toplevel(self.root)
        analysis_window.title("算法分析")
        analysis_window.geometry("600x500")

        frame = ttk.Frame(analysis_window, padding="10")
        frame.pack(fill=tk.BOTH, expand=True)

        # 创建文本区域
        text_widget = scrolledtext.ScrolledText(frame, wrap=tk.WORD, font=self.font)
        text_widget.pack(fill=tk.BOTH, expand=True)

        # 添加算法分析内容
        analysis_text = """
进程调度算法分析

1. 最短作业优先(SJF)算法

优点:
- 平均等待时间和周转时间通常最优,能有效提高系统吞吐量
- 短作业能够快速完成,减少了整体响应时间
- 在作业执行时间差异较大的情况下表现尤为出色

缺点:
- 需要预先知道作业执行时间,这在实际系统中往往难以实现
- 对长作业不利,可能导致长作业长时间得不到处理(饥饿现象)
- 无法适应动态到达的作业流

适用场景:
- 批处理系统,作业执行时间可预测的场景
- 作业执行时间差异较大的环境
- 对系统吞吐量要求较高的场景


2. 时间片轮转(RR)算法

优点:
- 公平性好,每个进程都能在一定时间内得到处理
- 响应时间快,适合交互式系统,用户体验较好
- 实现简单,不需要预先知道作业执行时间

缺点:
- 时间片设置过小将增加上下文切换开销,降低系统效率
- 时间片设置过大则退化为FCFS算法,失去RR的优势
- 平均周转时间通常比SJF长

适用场景:
- 分时系统,需要快速响应的交互式系统
- 作业执行时间相近的环境
- 对公平性要求较高的场景


3. 性能对比分析

在本次模拟中:
- SJF算法的平均等待时间为 {sjf_avg_wait:.2f}
- RR算法的平均等待时间为 {rr_avg_wait:.2f}
- SJF算法的平均周转时间为 {sjf_avg_turn:.2f}
- RR算法的平均周转时间为 {rr_avg_turn:.2f}

结论:
- 如果作业执行时间可预测,且追求最小的平均等待时间和周转时间,SJF是更好的选择
- 如果系统需要快速响应,保证公平性,RR是更合适的算法
- 时间片大小对RR算法的性能影响很大,需要根据系统特性合理设置

选择调度算法时,需要根据系统类型、作业特性和性能目标综合考虑。
        """.format(
            sjf_avg_wait=self.sjf_avg_wait,
            rr_avg_wait=self.rr_avg_wait,
            sjf_avg_turn=self.sjf_avg_turn,
            rr_avg_turn=self.rr_avg_turn
        )

        text_widget.insert(tk.END, analysis_text)
        text_widget.config(state=tk.DISABLED)  # 设为只读

        # 添加关闭按钮
        ttk.Button(frame, text="关闭", command=analysis_window.destroy).pack(pady=10)


def main():
    root = tk.Tk()
    app = ProcessSchedulingApp(root)
    root.mainloop()


if __name__ == "__main__":
    main()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值