fix: 修复进程调度模拟器的CPU利用率计算和深拷贝等bug

- base.py: CPU利用率改用 min(arrival)/max(completion),total_cpu_time改用 burst_time
- main.py: 深拷贝改用 copy.deepcopy 隔离列表引用,帮助文本默认值修正,演示数据 burst_time 对齐
- sjf.py: 实现 SRTF 抢占模式(逐时间单位执行)
- priority.py: 实现抢占式优先级调度
- rr.py/mlfq.py: 时间片用完回写 cpu_bursts[idx],io_waiting 字典改用 pid 作 key
This commit is contained in:
ViperEkura 2026-05-04 23:17:42 +08:00
parent 001416e399
commit 4897bef9a6
6 changed files with 173 additions and 76 deletions

View File

@ -54,7 +54,7 @@ class Process:
@property @property
def total_cpu_time(self) -> int: def total_cpu_time(self) -> int:
"""总CPU计算时间""" """总CPU计算时间"""
return sum(self.cpu_bursts) if self.cpu_bursts else self.burst_time return self.burst_time
@property @property
def total_io_time(self) -> int: def total_io_time(self) -> int:
@ -230,7 +230,9 @@ class ProcessScheduler:
# CPU 利用率计算 (考虑IO模拟) # CPU 利用率计算 (考虑IO模拟)
total_cpu_time = sum(p.total_cpu_time for p in self.results) # 纯CPU时间 total_cpu_time = sum(p.total_cpu_time for p in self.results) # 纯CPU时间
total_time = self.results[-1].completion_time - self.results[0].arrival_time first_arrival = min(p.arrival_time for p in self.results)
last_completion = max(p.completion_time for p in self.results)
total_time = last_completion - first_arrival
# CPU利用率 = CPU执行时间 / 总时间 # CPU利用率 = CPU执行时间 / 总时间
# 注意: IO期间CPU可以运行其他进程所以利用率应该更高 # 注意: IO期间CPU可以运行其他进程所以利用率应该更高
utilization = (total_cpu_time / total_time) * 100 if total_time > 0 else 0 utilization = (total_cpu_time / total_time) * 100 if total_time > 0 else 0

View File

@ -10,13 +10,14 @@
python main.py --demo # 使用默认演示数据 python main.py --demo # 使用默认演示数据
参数说明: 参数说明:
-n, --num: 进程数量 (默认 5) -n, --num: 进程数量 (默认 10)
-s, --seed: 随机种子 (默认 42, 设为 None 则随机) -s, --seed: 随机种子 (默认 42, 设为 None 则随机)
-a, --algo: 运行的算法 (默认全部) -a, --algo: 运行的算法 (默认全部)
--demo: 使用预设演示数据 --demo: 使用预设演示数据
""" """
import argparse import argparse
import copy
from typing import List, Dict, Optional from typing import List, Dict, Optional
from base import ( from base import (
Process, ProcessScheduler, Process, ProcessScheduler,
@ -38,12 +39,12 @@ DEMO_PROCESSES = [
Process(pid='P1', arrival_time=0, burst_time=7, priority=3, Process(pid='P1', arrival_time=0, burst_time=7, priority=3,
cpu_bursts=[7], io_bursts=[], is_io_bound=False), cpu_bursts=[7], io_bursts=[], is_io_bound=False),
# IO密集型进程: CPU -> IO -> CPU -> IO -> CPU # IO密集型进程: CPU -> IO -> CPU -> IO -> CPU
Process(pid='P2', arrival_time=2, burst_time=10, priority=1, Process(pid='P2', arrival_time=2, burst_time=8, priority=1,
cpu_bursts=[3, 2, 3], io_bursts=[5, 4], is_io_bound=True), cpu_bursts=[3, 2, 3], io_bursts=[5, 4], is_io_bound=True),
Process(pid='P3', arrival_time=4, burst_time=1, priority=4, Process(pid='P3', arrival_time=4, burst_time=1, priority=4,
cpu_bursts=[1], io_bursts=[], is_io_bound=False), cpu_bursts=[1], io_bursts=[], is_io_bound=False),
# IO密集型进程 # IO密集型进程
Process(pid='P4', arrival_time=5, burst_time=8, priority=2, Process(pid='P4', arrival_time=5, burst_time=6, priority=2,
cpu_bursts=[2, 2, 2], io_bursts=[6, 5], is_io_bound=True), cpu_bursts=[2, 2, 2], io_bursts=[6, 5], is_io_bound=True),
Process(pid='P5', arrival_time=6, burst_time=2, priority=3, Process(pid='P5', arrival_time=6, burst_time=2, priority=3,
cpu_bursts=[2], io_bursts=[], is_io_bound=False), cpu_bursts=[2], io_bursts=[], is_io_bound=False),
@ -57,6 +58,7 @@ def get_algorithm_config() -> Dict:
'SJF': lambda p: SJFScheduler(p, preemptive=False), 'SJF': lambda p: SJFScheduler(p, preemptive=False),
'SRTF': lambda p: SJFScheduler(p, preemptive=True), 'SRTF': lambda p: SJFScheduler(p, preemptive=True),
'Priority': lambda p: PriorityScheduler(p, preemptive=False), 'Priority': lambda p: PriorityScheduler(p, preemptive=False),
'PriorityP': lambda p: PriorityScheduler(p, preemptive=True),
'RR': lambda p: RoundRobinScheduler(p, time_slice=4), 'RR': lambda p: RoundRobinScheduler(p, time_slice=4),
'MLFQ': lambda p: MLFQScheduler(p), 'MLFQ': lambda p: MLFQScheduler(p),
'HRRN': HRRNScheduler, 'HRRN': HRRNScheduler,
@ -65,8 +67,8 @@ def get_algorithm_config() -> Dict:
def run_all_algorithms(processes: List[Process], algorithms: Optional[List[str]] = None): def run_all_algorithms(processes: List[Process], algorithms: Optional[List[str]] = None):
"""运行所有/指定调度算法并比较""" """运行所有/指定调度算法并比较"""
# 深拷贝进程列表 # 深拷贝进程列表 (使用 copy.deepcopy 确保 cpu_bursts/io_bursts 等列表字段独立)
original_processes = [Process(**p.__dict__) for p in processes] original_processes = [copy.deepcopy(p) for p in processes]
# 默认运行所有算法 # 默认运行所有算法
if algorithms is None: if algorithms is None:
@ -80,8 +82,8 @@ def run_all_algorithms(processes: List[Process], algorithms: Optional[List[str]]
print(f"警告: 未知算法 '{algo_name}', 跳过") print(f"警告: 未知算法 '{algo_name}', 跳过")
continue continue
# 每次重新创建进程列表 # 每次重新创建进程列表 (深拷贝确保各算法互不影响)
process_list = [Process(**p.__dict__) for p in original_processes] process_list = [copy.deepcopy(p) for p in original_processes]
# 获取调度器 # 获取调度器
scheduler_class = scheduler_map[algo_name] scheduler_class = scheduler_map[algo_name]
@ -122,7 +124,7 @@ def main():
) )
parser.add_argument('-n', '--num', type=int, default=10, parser.add_argument('-n', '--num', type=int, default=10,
help='随机生成的进程数量 (默认: 5)') help='随机生成的进程数量 (默认: 10)')
parser.add_argument('-s', '--seed', type=int, default=42, parser.add_argument('-s', '--seed', type=int, default=42,
help='随机种子 (默认: 42, 设为 None 则每次不同)') help='随机种子 (默认: 42, 设为 None 则每次不同)')
parser.add_argument('-a', '--algo', type=str, default=None, parser.add_argument('-a', '--algo', type=str, default=None,

View File

@ -43,7 +43,8 @@ class MLFQScheduler(ProcessScheduler):
queues = [deque() for _ in range(self.num_queues)] queues = [deque() for _ in range(self.num_queues)]
completed = [] completed = []
io_waiting = {} # {进程: IO完成时间} io_waiting = {} # {pid: IO完成时间}
process_map = {p.pid: p for p in all_processes} # pid -> Process 映射
current_time = 0 current_time = 0
max_iterations = 10000 # 防止死循环 max_iterations = 10000 # 防止死循环
@ -63,15 +64,16 @@ class MLFQScheduler(ProcessScheduler):
p.status = self.STATUS_READY p.status = self.STATUS_READY
queues[p.queue_idx].append(p) queues[p.queue_idx].append(p)
# 2. 检查 IO 完成 # 2. 检查 IO 完成 (兜底,防止事件队列遗漏)
completed_io = [] completed_io = []
for p, io_end_time in io_waiting.items(): for pid, io_end_time in list(io_waiting.items()):
if io_end_time <= current_time: if io_end_time <= current_time:
completed_io.append(p) completed_io.append(pid)
for p in completed_io: for pid in completed_io:
io_waiting.pop(p.pid, None) io_waiting.pop(pid, None)
p.status = self.STATUS_READY proc = process_map[pid]
queues[p.queue_idx].append(p) proc.status = self.STATUS_READY
queues[proc.queue_idx].append(proc)
# 3. 找最高优先级非空队列 # 3. 找最高优先级非空队列
q_idx = -1 q_idx = -1
@ -121,6 +123,8 @@ class MLFQScheduler(ProcessScheduler):
self.calculate_metrics(p, current_time) self.calculate_metrics(p, current_time)
completed.append(p) completed.append(p)
else: else:
# 时间片用完,更新剩余 burst
p.cpu_bursts[p.current_cpu_idx] = cpu_burst - exec_t
p.status = self.STATUS_READY p.status = self.STATUS_READY
nq = min(q_idx + 1, self.num_queues - 1) nq = min(q_idx + 1, self.num_queues - 1)
p.queue_idx = nq p.queue_idx = nq

View File

@ -35,7 +35,7 @@ class PriorityScheduler(ProcessScheduler):
for p in all_processes: for p in all_processes:
heapq.heappush(events, (p.arrival_time, 'arrival', id(p), p)) heapq.heappush(events, (p.arrival_time, 'arrival', id(p), p))
ready_queue = [] # (priority, pid, process) ready_queue = [] # (priority, id, process)
completed = [] completed = []
current_time = 0 current_time = 0
@ -59,6 +59,49 @@ class PriorityScheduler(ProcessScheduler):
else: else:
break break
if self.preemptive:
# 抢占式: 一次只执行1个时间单位随时检查更高优先级的到达进程
_, _, current_process = ready_queue[0]
if current_process.start_time == -1:
current_process.start_time = current_time
current_process.status = self.STATUS_RUNNING
if current_process.current_cpu_idx < len(current_process.cpu_bursts):
cpu_burst = current_process.cpu_bursts[current_process.current_cpu_idx]
exec_t = 1
self.gantt_chart.append((current_time, current_process.pid, 'CPU', exec_t))
current_time += exec_t
current_process.remaining_cpu_time -= exec_t
cpu_burst -= exec_t
current_process.cpu_bursts[current_process.current_cpu_idx] = cpu_burst
if cpu_burst == 0:
heapq.heappop(ready_queue) # 从就绪队列移除
current_process.current_cpu_idx += 1
io_idx = current_process.current_cpu_idx - 1
if io_idx < len(current_process.io_bursts):
io_time = current_process.io_bursts[io_idx]
current_process.status = self.STATUS_IO_WAIT
heapq.heappush(events, (current_time + io_time, 'io_complete', id(current_process), current_process))
else:
current_process.status = self.STATUS_TERMINATED
current_process.completion_time = current_time
self.calculate_metrics(current_process, current_time)
completed.append(current_process)
else:
current_process.status = self.STATUS_READY
else:
heapq.heappop(ready_queue)
current_process.status = self.STATUS_TERMINATED
current_process.completion_time = current_time
self.calculate_metrics(current_process, current_time)
completed.append(current_process)
else:
# 非抢占式: 一次执行完整CPU burst
_, _, current_process = heapq.heappop(ready_queue) _, _, current_process = heapq.heappop(ready_queue)
if current_process.start_time == -1: if current_process.start_time == -1:
@ -92,7 +135,8 @@ class PriorityScheduler(ProcessScheduler):
completed.append(current_process) completed.append(current_process)
self.results = completed self.results = completed
return self.print_results("Priority (优先级调度)") algo_name = "Priority (抢占式)" if self.preemptive else "Priority (非抢占式)"
return self.print_results(algo_name)
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -37,7 +37,8 @@ class RoundRobinScheduler(ProcessScheduler):
ready_queue = deque() ready_queue = deque()
completed = [] completed = []
io_waiting = {} # {进程PID: IO完成时间} io_waiting = {} # {pid: IO完成时间}
process_map = {p.pid: p for p in all_processes} # pid -> Process 映射
current_time = 0 current_time = 0
max_iterations = 10000 max_iterations = 10000
@ -57,15 +58,16 @@ class RoundRobinScheduler(ProcessScheduler):
p.status = self.STATUS_READY p.status = self.STATUS_READY
ready_queue.append(p) ready_queue.append(p)
# 2. 检查 IO 完成 # 2. 检查 IO 完成 (兜底,防止事件队列遗漏)
completed_io = [] completed_io = []
for p, io_end_time in io_waiting.items(): for pid, io_end_time in list(io_waiting.items()):
if io_end_time <= current_time: if io_end_time <= current_time:
completed_io.append(p) completed_io.append(pid)
for p in completed_io: for pid in completed_io:
io_waiting.pop(p.pid, None) io_waiting.pop(pid, None)
p.status = self.STATUS_READY proc = process_map[pid]
ready_queue.append(p) proc.status = self.STATUS_READY
ready_queue.append(proc)
if not ready_queue: if not ready_queue:
# 队列为空,推进时间 # 队列为空,推进时间
@ -93,6 +95,7 @@ class RoundRobinScheduler(ProcessScheduler):
p.remaining_cpu_time -= exec_t p.remaining_cpu_time -= exec_t
if exec_t >= cpu_burst: if exec_t >= cpu_burst:
# CPU burst 完成
p.current_cpu_idx += 1 p.current_cpu_idx += 1
io_idx = p.current_cpu_idx - 1 io_idx = p.current_cpu_idx - 1
if io_idx < len(p.io_bursts): if io_idx < len(p.io_bursts):
@ -106,6 +109,8 @@ class RoundRobinScheduler(ProcessScheduler):
self.calculate_metrics(p, current_time) self.calculate_metrics(p, current_time)
completed.append(p) completed.append(p)
else: else:
# 时间片用完,更新剩余 burst
p.cpu_bursts[p.current_cpu_idx] = cpu_burst - exec_t
p.status = self.STATUS_READY p.status = self.STATUS_READY
ready_queue.append(p) ready_queue.append(p)
else: else:

View File

@ -36,18 +36,16 @@ class SJFScheduler(ProcessScheduler):
for p in all_processes: for p in all_processes:
self.init_process(p, p.arrival_time) self.init_process(p, p.arrival_time)
# 事件队列: (时间, 事件类型, 唯一ID, 进程)
events = [] events = []
for p in all_processes: for p in all_processes:
heapq.heappush(events, (p.arrival_time, 'arrival', id(p), p)) heapq.heappush(events, (p.arrival_time, 'arrival', id(p), p))
ready_queue = [] # 就绪队列 (需要排序) ready_queue = []
completed = [] completed = []
current_time = 0 current_time = 0
while len(completed) < len(all_processes): while len(completed) < len(all_processes):
# 处理事件
while events and events[0][0] <= current_time: while events and events[0][0] <= current_time:
event_time, event_type, uid, p = heapq.heappop(events) event_time, event_type, uid, p = heapq.heappop(events)
@ -66,8 +64,51 @@ class SJFScheduler(ProcessScheduler):
else: else:
break break
# 按剩余时间排序
ready_queue.sort(key=lambda p: (p.remaining_cpu_time, id(p))) ready_queue.sort(key=lambda p: (p.remaining_cpu_time, id(p)))
if self.preemptive:
# SRTF: 一次只执行1个时间单位随时检查新到达进程
current_process = ready_queue[0]
if current_process.start_time == -1:
current_process.start_time = current_time
current_process.status = self.STATUS_RUNNING
if current_process.current_cpu_idx < len(current_process.cpu_bursts):
cpu_burst = current_process.cpu_bursts[current_process.current_cpu_idx]
exec_t = 1 # 每次只执行1单位
self.gantt_chart.append((current_time, current_process.pid, 'CPU', exec_t))
current_time += exec_t
current_process.remaining_cpu_time -= exec_t
cpu_burst -= exec_t
current_process.cpu_bursts[current_process.current_cpu_idx] = cpu_burst
if cpu_burst == 0:
ready_queue.pop(0) # CPU burst完成移出就绪队列
current_process.current_cpu_idx += 1
io_idx = current_process.current_cpu_idx - 1
if io_idx < len(current_process.io_bursts):
io_time = current_process.io_bursts[io_idx]
current_process.status = self.STATUS_IO_WAIT
heapq.heappush(events, (current_time + io_time, 'io_complete', id(current_process), current_process))
else:
current_process.status = self.STATUS_TERMINATED
current_process.completion_time = current_time
self.calculate_metrics(current_process, current_time)
completed.append(current_process)
else:
current_process.status = self.STATUS_READY
else:
ready_queue.pop(0)
current_process.status = self.STATUS_TERMINATED
current_process.completion_time = current_time
self.calculate_metrics(current_process, current_time)
completed.append(current_process)
else:
# SJF 非抢占: 一次执行完整CPU burst
current_process = ready_queue.pop(0) current_process = ready_queue.pop(0)
if current_process.start_time == -1: if current_process.start_time == -1:
@ -75,7 +116,6 @@ class SJFScheduler(ProcessScheduler):
current_process.status = self.STATUS_RUNNING current_process.status = self.STATUS_RUNNING
# 执行 CPU burst
if current_process.current_cpu_idx < len(current_process.cpu_bursts): if current_process.current_cpu_idx < len(current_process.cpu_bursts):
cpu_burst = current_process.cpu_bursts[current_process.current_cpu_idx] cpu_burst = current_process.cpu_bursts[current_process.current_cpu_idx]