当前位置: 首页 > news >正文

操作系统:实验一:进程调度算法模拟——动态优先数与PCB管理的C语言实现

1. 动态优先数调度算法基础

动态优先数调度算法是操作系统中常见的进程调度策略之一,它的核心思想是根据进程的优先级动态分配CPU资源。与静态优先级不同,动态优先数会随着进程运行状态的变化而调整,这使得系统能够更灵活地响应不同进程的需求。

在实现动态优先数调度时,每个进程都会被赋予一个初始优先数(通常数值越大优先级越高)。当一个进程获得CPU时间片执行后,如果它没有完成全部工作,系统会降低它的优先数。这样做的目的是防止高优先级进程长期独占CPU资源,从而提高系统的公平性。

举个例子,假设系统中有三个进程A、B、C,初始优先数分别为5、3、1。第一轮调度时,进程A会最先执行。执行一个时间片后,如果A还未完成,它的优先数降为4。此时三个进程的优先数变为4、3、1,下一轮调度时A仍然优先级最高。只有当A的优先数降到低于B时,B才有机会获得CPU资源。

2. PCB设计与进程状态管理

2.1 PCB数据结构设计

进程控制块(PCB)是操作系统中管理进程的核心数据结构,它包含了系统管理进程所需的全部信息。在我们的C语言实现中,PCB结构体定义如下:

typedef struct pcb { char name[10]; // 进程名 char state; // 进程状态(W:就绪, R:运行, F:完成) int priority; // 动态优先数 int total_time; // 需要运行的总时间 int elapsed_time; // 已运行时间 struct pcb *next; // 指向下一个PCB的指针 } PCB;

这个结构体包含了进程调度的关键信息:

  • name字段标识进程,方便调试和输出
  • state字段记录进程当前状态,实现三态转换
  • priority字段存储动态变化的优先数
  • 两个时间字段实现时间片管理
  • next指针用于构建就绪队列

2.2 进程状态转换逻辑

在我们的模拟系统中,进程有三种基本状态:

  • 就绪态(W):进程已准备好,等待CPU资源
  • 运行态(R):进程正在CPU上执行
  • 完成态(F):进程已完成全部工作

状态转换规则如下:

  1. 新创建的进程初始状态为就绪态(W)
  2. 调度器从就绪队列选择优先级最高的进程,将其状态改为运行态(R)
  3. 进程运行一个时间片后:
    • 如果已运行时间等于总时间,转为完成态(F)
    • 否则优先数减1,转回就绪态(W)

这种状态转换机制通过简单的条件判断即可实现:

if(p->elapsed_time == p->total_time) { p->state = 'F'; // 转为完成态 free(p); // 释放PCB } else { p->priority--; // 优先数减1 p->state = 'W'; // 转回就绪态 insert_to_ready_queue(p); // 重新插入就绪队列 }

3. 调度器核心实现

3.1 就绪队列管理

就绪队列采用单向链表结构组织,新进程加入时需要根据优先级找到合适的位置。我们实现了一个排序插入函数:

void insert_by_priority(PCB **queue, PCB *process) { process->next = NULL; // 队列为空或新进程优先级最高 if(*queue == NULL || process->priority > (*queue)->priority) { process->next = *queue; *queue = process; return; } // 寻找插入位置 PCB *current = *queue; while(current->next != NULL && process->priority <= current->next->priority) { current = current->next; } // 插入新进程 process->next = current->next; current->next = process; }

这个函数保证了就绪队列始终按优先级从高到低排列,调度时只需取队列首元素即可获得最高优先级进程。

3.2 调度循环实现

主调度循环负责不断从就绪队列选取进程执行,直到所有进程完成。基本流程如下:

void schedule() { int time_slice = 0; while(ready_queue != NULL) { time_slice++; printf("\n--- 时间片 %d ---\n", time_slice); // 获取最高优先级进程 PCB *current = ready_queue; ready_queue = ready_queue->next; current->next = NULL; // 执行进程 run_process(current); // 处理执行后状态 if(current->state != 'F') { insert_by_priority(&ready_queue, current); } } printf("\n所有进程执行完成!总时间片:%d\n", time_slice); }

每次循环代表一个时间片的执行,主要完成以下工作:

  1. 从就绪队列取出队首进程
  2. 执行该进程(增加已运行时间)
  3. 根据执行结果更新进程状态
  4. 未完成的进程重新插入就绪队列

4. 完整代码实现与测试

4.1 完整源代码

以下是整合了所有功能的完整实现:

#include <stdio.h> #include <stdlib.h> typedef struct pcb { char name[10]; char state; int priority; int total_time; int elapsed_time; struct pcb *next; } PCB; PCB *ready_queue = NULL; void insert_by_priority(PCB **queue, PCB *process) { process->next = NULL; if(*queue == NULL || process->priority > (*queue)->priority) { process->next = *queue; *queue = process; return; } PCB *current = *queue; while(current->next != NULL && process->priority <= current->next->priority) { current = current->next; } process->next = current->next; current->next = process; } void create_processes() { int count; printf("输入进程数量: "); scanf("%d", &count); for(int i = 0; i < count; i++) { PCB *p = (PCB *)malloc(sizeof(PCB)); printf("输入进程%d名称、优先数、总时间: ", i+1); scanf("%s %d %d", p->name, &p->priority, &p->total_time); p->state = 'W'; p->elapsed_time = 0; p->next = NULL; insert_by_priority(&ready_queue, p); } } void print_queue() { PCB *current = ready_queue; printf("就绪队列: "); while(current != NULL) { printf("%s(P%d)->", current->name, current->priority); current = current->next; } printf("NULL\n"); } void run_process(PCB *process) { process->state = 'R'; process->elapsed_time++; printf("执行进程: %s\n", process->name); printf("状态: %c, 优先数: %d, 已运行/总时间: %d/%d\n", process->state, process->priority, process->elapsed_time, process->total_time); if(process->elapsed_time >= process->total_time) { printf("进程 %s 完成!\n", process->name); process->state = 'F'; free(process); } else { process->priority--; process->state = 'W'; } } void schedule() { int time_slice = 0; while(ready_queue != NULL) { time_slice++; printf("\n--- 时间片 %d ---\n", time_slice); print_queue(); PCB *current = ready_queue; ready_queue = ready_queue->next; current->next = NULL; run_process(current); if(current->state != 'F') { insert_by_priority(&ready_queue, current); } } printf("\n所有进程执行完成!总时间片:%d\n", time_slice); } int main() { create_processes(); schedule(); return 0; }

4.2 测试用例与结果分析

我们使用以下测试数据验证调度器:

进程数量: 3 进程1: A 3 2 进程2: B 1 1 进程3: C 2 3

预期执行顺序:

  1. 时间片1: A(3)执行,优先数降为2
  2. 时间片2: C(2)执行,优先数降为1
  3. 时间片3: A(2)执行,优先数降为1,同时完成
  4. 时间片4: B(1)和C(1)竞争,B先执行并完成
  5. 时间片5: C(1)执行,优先数降为0
  6. 时间片6: C(0)执行并完成

实际运行输出与预期完全一致,验证了调度算法的正确性。通过这种逐步执行和状态检查的方式,可以确保动态优先数调度算法的每个细节都正确实现。

5. 扩展与优化思路

5.1 算法优化方向

当前实现的时间复杂度主要来自就绪队列的插入操作。当进程数量较多时,可以考虑以下优化:

  1. 使用优先队列(堆)数据结构管理就绪队列,将插入和提取操作的时间复杂度从O(n)降到O(log n)
  2. 实现多级反馈队列,将不同优先级的进程分组管理
  3. 添加时间片耗尽检查,防止单个进程运行时间过长

5.2 功能扩展建议

为了使模拟系统更接近真实场景,可以考虑添加以下功能:

  1. 进程阻塞机制:增加I/O等待状态,模拟进程等待外部事件
  2. 优先级提升策略:对长时间未执行的进程适当提升优先级,防止饥饿
  3. 更丰富的进程创建方式:支持从文件读取进程描述,或随机生成测试用例
  4. 可视化界面:使用图形化方式展示调度过程和队列状态变化

这些扩展能够帮助学生更深入地理解操作系统调度机制的复杂性,为后续学习更高级的调度算法打下基础。

http://www.jsqmd.com/news/1197758/

相关文章:

  • ILLA Builder企业级部署:Docker+PostgreSQL生产实践指南
  • 豆包生成文件本地存储路径全平台详解
  • WSL2+Ubuntu22.04部署Hermes Agent全指南
  • C++11并发编程实战:从std::thread到线程池的完整指南
  • OpenCode终端AI编程副驾驶:5分钟精准配置指南
  • C/C++编程实战 - 字符串比较进阶:手写一个不区分大小写的strcmp
  • C++高性能日志系统设计:多线程异步架构与设计模式实践
  • 腾讯云轻量服务器部署DeepSeek-TUI实战指南
  • 计算巢上OpenClaw与Hermes Agent共部署实战指南
  • Python科学计算第二章 Python序列
  • 【数字孪生实战案例】智慧停车系统如何清晰展示车位占用、空余状态等实时车位信息?~山海鲸可视化
  • VS Code配置MinGW:从零搭建Windows C语言开发环境
  • DevEco Code 双 Agent 协同:Plan+Build 模式提效实战
  • 想让Claude不再说“load - bearing”?试试`MessageDisplay`钩子!
  • MinGW-w64安装配置实战:Windows下轻量C/C++编译环境搭建指南
  • Navicat集成Kimi AI助手实战指南:国产大模型如何提升SQL效率
  • 进制转换:程序员必备的内功心法,C/Java实现详解
  • 保险丝工作原理与选型指南:从基础到实战应用
  • EKS生产就绪:从AWS基础设施到Kubernetes集群的全链路加固
  • 2026年7月最新亨得利官方服务项目及价格查询|完整热线和详细地址权威信息公告 - 亨得利官方
  • 《计算机组成原理》课程设计实战:基于TEC-2与AM2901的微程序指令扩展
  • Scout与OpenClaw:企业级AI代理的工作流操作系统
  • RStudio 2025:专业数据科学IDE的物理安装与工作流激活
  • Copilot深度集成WebView2技术解析与企业部署指南
  • SpringBoot - 深度解析POST请求参数绑定与数据转换
  • 从“或”到“且”:概率加法公式的实战拆解与场景化应用
  • Cell Reports Medicine IF=11.7 | CANDiT:机器学习框架推动结直肠癌分化治疗
  • 抖音高流水主播的三条路:个人、公会与挂靠的真实账本 - 彭拜新闻(测评)
  • 从 GitHub Actions 到本地兜底发布:AI Mind 容器化上线的一次真实收口复盘
  • cc-switch 实战指南:Mac 本地大模型路由与 Claude API 集成