【操作系统】进程与线程管理

1. 进程(Process)

定义:进程是程序的执行实例,是系统资源分配的基本单位。每个进程有独立的地址空间、代码段、数据段和堆栈。

状态

  • 就绪(Ready):等待调度器分配CPU
  • 运行(Running):正在被CPU执行
  • 阻塞(Blocked):等待I/O等事件完成

典型操作

  • 创建:使用fork()系统调用生成子进程
  • 终止:exit()结束进程
  • 切换:由操作系统调度器实现上下文切换

2. 线程(Thread)

定义:线程是CPU调度和分派的基本单位,是进程中的执行流。多个线程共享同一进程的资源(如内存空间、打开的文件)。

多线程的优点

  • 响应性强,资源开销小
  • 适合并行处理
  • 线程间通信更高效(共享变量,无需IPC)

示例代码(C++11 std::thread)

#include <iostream>
#include <thread>

void task(int id) {
    std::cout << "Thread " << id << " is running\n";
}

int main() {
    std::thread t1(task, 1);
    std::thread t2(task, 2);
    t1.join();
    t2.join();
    return 0;
}

3. 调度算法

所有调度模拟均基于如下进程结构体:

struct Process {
    int pid;
    int arrival_time;
    int burst_time;
    int priority;
    int remaining_time; // 仅对抢占式或RR有用
};

FCFS(First-Come, First-Served)调度算法详解

一、调度原理

FCFS(先来先服务)是一种最基本、最简单的进程调度算法。其核心思想是:按照进程到达的先后顺序依次执行进程,一个进程执行完毕后,才调度下一个进程执行。

具体特点:

  • 非抢占式:一旦某个进程被调度执行,将一直占用CPU直到完成。
  • 简单公平:谁先到谁先服务。
  • 缺点明显:可能导致“长作业堵塞短作业”(即Convoy Effect,护航效应)。
二、调度流程
  1. 将所有进程按 arrival_time(到达时间)从小到大排序;
  2. 遍历排好序的进程队列,依次执行每个进程;
  3. 若某进程到达时间晚于当前时间,CPU空闲,等待进程到来;
  4. 累加每个进程的 burst_time(执行时间)以计算调度起点和终点。
三、举例说明
进程列表:
PID到达时间(arrival_time)执行时间(burst_time)
P104
P213
P322
P431
执行过程(按到达顺序):

排序结果为:

P1 (0), P2 (1), P3 (2), P4 (3)
执行步骤:
  • 时间0:P1 到达并执行,运行时间4 → 结束于时间4
  • 时间4:P2 已经到达 → 执行,运行时间3 → 结束于时间7
  • 时间7:P3 已经到达 → 执行,运行时间2 → 结束于时间9
  • 时间9:P4 已经到达 → 执行,运行时间1 → 结束于时间10
四、代码实现解释(C++)
void FCFS(std::vector<Process>& plist) {
    std::sort(plist.begin(), plist.end(), [](Process a, Process b) {
        return a.arrival_time < b.arrival_time;
    });

    int current_time = 0;
    for (auto& p : plist) {
        if (current_time < p.arrival_time)
            current_time = p.arrival_time; // 如果CPU空闲,则等待该进程到达
        std::cout << "Process " << p.pid << " starts at " << current_time << "\n";
        current_time += p.burst_time;
    }
}

说明:

  • std::sort:确保进程按到达时间先后排列;
  • current_time < p.arrival_time:处理进程到达前CPU空闲的情形;
  • current_time += burst_time:模拟进程完整运行后累加时间。
五、评价
优点缺点
实现简单,顺序清晰长进程阻塞短进程,平均等待时间高
没有上下文切换开销不适合实时、交互式应用场景

SJF(Shortest Job First,非抢占)

原理:从就绪队列中选择运行时间最短的进程。

void SJF(std::vector<Process> plist) {
    int time = 0;
    std::vector<Process> ready;

    while (!plist.empty() || !ready.empty()) {
        for (auto it = plist.begin(); it != plist.end();) {
            if (it->arrival_time <= time) {
                ready.push_back(*it);
                it = plist.erase(it);
            } else ++it;
        }

        if (ready.empty()) {
            time++;
            continue;
        }

        auto it = std::min_element(ready.begin(), ready.end(),
            [](Process a, Process b) { return a.burst_time < b.burst_time; });

        std::cout << "Process " << it->pid << " runs at " << time << "\n";
        time += it->burst_time;
        ready.erase(it);
    }
}
SJF(Shortest Job First)调度算法
一、调度原理

SJF(Shortest Job First),即“短作业优先调度算法”,其核心思想是:

从就绪队列中选择“执行时间(burst_time)最短”的进程先执行

根据是否抢占,分为两种:

  1. 非抢占式 SJF(本节重点):当前运行的进程一旦开始,必须运行完才能调度下一个。
  2. 抢占式 SJF(又称 SRTF):如果有更短作业到达,会抢占当前执行进程。
二、调度策略说明

调度器会在每一个时刻,检查就绪队列中哪些进程已到达,然后从中选出 burst_time 最短的进程执行。

三、举例说明(非抢占 SJF)
进程列表:
PID到达时间执行时间(burst_time)
P108
P214
P322
P431
执行流程:
  • 时间 0:P1 到达,开始执行,持续到时间 8
  • 时间 1-3:P2、P3、P4 陆续到达
  • 时间 8:选出 P4(执行时间1)→ 执行至 9
  • 时间 9:剩余 P3(2)与 P2(4),选 P3 → 执行至 11
  • 时间 11:P2 执行至 15
四、C++ 实现代码(非抢占式 SJF)
#include <iostream>
#include <vector>
#include <algorithm>

struct Process {
    int pid;
    int arrival_time;
    int burst_time;
};

void SJF(std::vector<Process> plist) {
    int time = 0;
    std::vector<Process> ready;

    while (!plist.empty() || !ready.empty()) {
        for (auto it = plist.begin(); it != plist.end();) {
            if (it->arrival_time <= time) {
                ready.push_back(*it);
                it = plist.erase(it);
            } else ++it;
        }

        if (ready.empty()) {
            time++;
            continue;
        }

        auto it = std::min_element(ready.begin(), ready.end(),
            [](Process a, Process b) { return a.burst_time < b.burst_time; });

        std::cout << "Process " << it->pid << " runs at " << time << "\n";
        time += it->burst_time;
        ready.erase(it);
    }
}
五、关键点解释
  • plist 存放所有尚未到达的进程
  • 每次循环把“到达时间 <= 当前时间”的进程转入 ready 队列
  • ready 队列中选择 burst_time 最短的进程执行
  • ready 队列为空,表示 CPU 空闲,时间 time++ 跳过等待
六、优缺点分析
优点缺点
能最小化平均等待时间需要提前知道每个进程的执行时间
相对公平:短进程不再被长进程阻塞容易造成长进程“饥饿”(starvation)
提高系统吞吐率不适用于实时系统,响应时间不可控
RR(Round Robin,时间片轮转)调度算法
一、调度原理

时间片轮转调度(Round Robin, RR) 是一种抢占式调度算法,特别适用于多用户交互式系统

核心思想是:为每个进程分配固定长度的 时间片(Time Quantum),按到达顺序循环执行。若进程在时间片内未完成,则被抢占并重新排入队尾。

二、调度机制
  1. 所有到达的进程按照顺序进入一个队列(就绪队列)
  2. CPU 从队列头部取出一个进程,执行时间片长度
  3. 若进程执行完毕,终止;否则,放回队尾
  4. 周而复始直到所有进程完成
三、示例说明(时间片 = 2)
进程列表:
PID到达时间执行时间(burst_time)
P105
P213
P321
执行过程:
  • 时间 0:P1 执行 2 → 剩余 3 → 入队
  • 时间 2:P2 执行 2 → 剩余 1 → 入队
  • 时间 4:P3 执行 1(完成)
  • 时间 5:P1 执行 2 → 剩余 1 → 入队
  • 时间 7:P2 执行 1(完成)
  • 时间 8:P1 执行 1(完成)
四、C++ 实现代码(带时间片控制)
#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>

struct Process {
    int pid;
    int arrival_time;
    int burst_time;
    int remaining_time;
};

void RR(std::vector<Process> plist, int quantum = 2) {
    int time = 0;
    std::queue<Process> q;

    // 初始化剩余时间
    for (auto& p : plist)
        p.remaining_time = p.burst_time;

    // 按到达时间排序
    std::sort(plist.begin(), plist.end(), [](Process a, Process b) {
        return a.arrival_time < b.arrival_time;
    });

    int index = 0;

    while (index < plist.size() || !q.empty()) {
        // 入队当前时刻到达的进程
        while (index < plist.size() && plist[index].arrival_time <= time) {
            q.push(plist[index]);
            index++;
        }

        if (q.empty()) {
            time++; // CPU 空闲
            continue;
        }

        Process p = q.front(); q.pop();
        int run_time = std::min(quantum, p.remaining_time);

        std::cout << "Process " << p.pid << " runs from " << time
                  << " to " << time + run_time << "\n";

        time += run_time;
        p.remaining_time -= run_time;

        // 新到进程再次入队
        while (index < plist.size() && plist[index].arrival_time <= time) {
            q.push(plist[index]);
            index++;
        }

        if (p.remaining_time > 0)
            q.push(p); // 没运行完就重新入队
    }
}
五、优缺点分析
优点缺点
所有进程公平获得 CPU 使用权时间片过小:频繁切换,系统开销大
响应时间短,适合交互式应用时间片过大:退化为 FCFS
实现简单,易于硬件支持无法动态识别进程重要性(例如高优先级任务)
六、关键控制参数
  • 时间片长度是该算法的关键性能参数:
    • 过短 → 上下文切换频繁,浪费 CPU
    • 过长 → 响应延迟上升,退化为 FCFS
优先级调度(Priority Scheduling)
一、调度原理

优先级调度算法根据进程的**优先级(Priority)**来选择哪个进程先执行:

  • 优先级数值越小,优先级越高
  • 可以是非抢占式(本节重点),也可以是抢占式
  • 每次调度时,从就绪队列中选出当前已到达、优先级最高的进程
二、优先级来源

优先级可以由系统分配,也可以由用户设定,通常基于以下标准:

  • 进程类型(I/O密集型 vs 计算密集型)
  • 等待时间(可用于动态调整优先级)
  • 用户级别(管理员进程可优先)
  • 任务重要性(实时任务优先)
三、调度过程举例(非抢占式)
进程列表:
PID到达时间执行时间优先级
P1042
P2131
P3213
P4322
执行步骤(非抢占式):
  • 时间 0:P1 到达,P1 执行
  • 时间 4:P2、P3、P4 已到达,优先级最高的是 P2 → 执行
  • 时间 7:P3 和 P4 在队列中,P4 优先级较高 → 执行
  • 时间 9:P3 → 执行
四、C++ 实现代码(非抢占式)
#include <iostream>
#include <vector>
#include <algorithm>

struct Process {
    int pid;
    int arrival_time;
    int burst_time;
    int priority;
};

void PrioritySchedule(std::vector<Process> plist) {
    int time = 0;
    std::vector<Process> ready;

    while (!plist.empty() || !ready.empty()) {
        // 将到达的进程加入就绪队列
        for (auto it = plist.begin(); it != plist.end();) {
            if (it->arrival_time <= time) {
                ready.push_back(*it);
                it = plist.erase(it);
            } else {
                ++it;
            }
        }

        // 若无进程可调度,CPU空闲
        if (ready.empty()) {
            time++;
            continue;
        }

        // 从就绪队列中选出优先级最高(数值最小)进程
        auto it = std::min_element(ready.begin(), ready.end(),
            [](Process a, Process b) {
                return a.priority < b.priority;
            });

        std::cout << "Process " << it->pid << " (priority " << it->priority
                  << ") runs from " << time << " to " << time + it->burst_time << "\n";
        time += it->burst_time;
        ready.erase(it);
    }
}
五、优缺点分析
优点缺点
控制灵活:可人为设定优先级实现任务重要性调度可能导致低优先级进程长期得不到执行(饥饿)
支持抢占与非抢占多种策略不公平:未考虑等待时间、到达顺序
易与其他策略结合:如优先级 + 时间片需要附加机制防止优先级反转(priority inversion)问题
六、典型优化机制(了解即可)
  • 优先级提升机制:等待时间越久,进程优先级逐步提高
  • 优先级继承机制:低优先级进程持有关键资源时临时“继承”高优先级
七、适用场景
  • 实时系统(如实时音视频流处理)
  • 后台任务调度(如守护进程 vs 用户进程)
多级反馈队列(Multilevel Feedback Queue,MFQ)调度算法
一、调度原理

多级反馈队列调度是一种结合多种策略的复杂调度机制,具有以下特点:

  1. 存在多个优先级队列(例如Q1、Q2、Q3…)
  2. 每个队列有不同的时间片长度(高优先级队列时间片短,低优先级时间片长)
  3. 所有新到达的进程从最高优先级队列开始
  4. 若进程在某队列的时间片内未完成,将被“降级”到下一级队列
  5. 低级队列的进程执行前,必须所有高级队列为空
二、调度机制
  • 结合了:FCFS + RR + 动态优先级调整
  • 是现代系统(如 Linux CFS, Windows)调度器的重要基础模型
三、流程图(示意)
新进程 → Q1 → Q2 → Q3
        ↑     ↑     ↑
        |     |     |
   完成 | 未完成    | 未完成
   within time slice → 降级 → 降级
四、示例(简化3进程,2个队列,时间片分别为2/4)
进程列表:
PID到达时间执行时间(burst_time)
P105
P213
P326
执行规则:
  • Q1:高优先级队列,时间片 2
  • Q2:低优先级队列,时间片 4
执行过程:
  • 时间 0:P1 到达 → Q1 执行 2 → 剩余 3 → 降至 Q2
  • 时间 2:P2 到达 → Q1 执行 2 → 剩余 1 → 降至 Q2
  • 时间 4:P3 到达 → Q1 空 → Q2 中 P1 → 执行 3 → 完成
  • 时间 7:Q2 中 P2 → 执行 1 → 完成
  • 时间 8:Q2 中 P3 → 执行 4 → 剩余 2 → 重新排入Q2
  • 时间 12:P3 → 执行 2 → 完成
五、C++ 实现代码(双队列简化版)
#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>

struct Process {
    int pid;
    int arrival_time;
    int burst_time;
    int remaining_time;
};

void MFQ(std::vector<Process> plist) {
    std::queue<Process> q1, q2;
    int time = 0;

    // 初始化剩余时间
    for (auto& p : plist)
        p.remaining_time = p.burst_time;

    // 按到达时间排序
    std::sort(plist.begin(), plist.end(), [](Process a, Process b) {
        return a.arrival_time < b.arrival_time;
    });

    int index = 0;
    while (index < plist.size() || !q1.empty() || !q2.empty()) {
        // 将到达的进程加入Q1
        while (index < plist.size() && plist[index].arrival_time <= time) {
            q1.push(plist[index]);
            index++;
        }

        if (!q1.empty()) {
            Process p = q1.front(); q1.pop();
            int run = std::min(2, p.remaining_time);
            std::cout << "Q1: Process " << p.pid << " runs from " << time << " to " << time + run << "\n";
            time += run;
            p.remaining_time -= run;

            while (index < plist.size() && plist[index].arrival_time <= time) {
                q1.push(plist[index]);
                index++;
            }

            if (p.remaining_time > 0)
                q2.push(p);
        } else if (!q2.empty()) {
            Process p = q2.front(); q2.pop();
            int run = std::min(4, p.remaining_time);
            std::cout << "Q2: Process " << p.pid << " runs from " << time << " to " << time + run << "\n";
            time += run;
            p.remaining_time -= run;

            while (index < plist.size() && plist[index].arrival_time <= time) {
                q1.push(plist[index]);
                index++;
            }

            if (p.remaining_time > 0)
                q2.push(p);
        } else {
            time++; // CPU 空闲
        }
    }
}
六、优缺点分析
优点缺点
综合考虑响应时间、公平性、吞吐量等性能指标实现复杂,需要动态管理多级队列
高优先级进程响应快低优先级进程可能饿死(可用优先级提升机制)
能模拟各种调度策略的组合形式系统开销较大
七、现代系统应用
  • **Windows、Linux调度器(CFS)**等都使用类似的多级反馈思想
  • 实际实现中可能有几十个调度等级 + 优先级动态调整机制

主函数统一调用示例:

int main() {
    std::vector<Process> plist = {
        {1, 0, 5, 2, 0},
        {2, 1, 3, 1, 0},
        {3, 2, 8, 3, 0},
        {4, 3, 6, 2, 0}
    };

    std::cout << "--- FCFS ---\n";
    FCFS(plist);

    std::cout << "\n--- SJF ---\n";
    SJF(plist);

    std::cout << "\n--- RR ---\n";
    RR(plist);

    std::cout << "\n--- Priority ---\n";
    PrioritySchedule(plist);

    std::cout << "\n--- MFQ ---\n";
    MFQ(plist);

    return 0;
}

4.调度算法总览

算法抢占性核心原理优点缺点适用场景
FCFS按到达顺序排队,先来先服务简单易实现长作业可能堵塞短作业(护航效应)批处理系统
SJF每次选择执行时间最短的进程最短平均等待时间需提前知道执行时间,易造成饥饿知道作业长度的系统
RR每个进程轮流执行,时间片控制公平性好,响应快时间片过短或过长都会带来效率问题多用户交互系统
Priority可选选择优先级最高(数值最低)的进程执行控制灵活,可引入动态调度策略饥饿现象明显,需补充优先级提升机制实时系统、关键任务优先场景
MFQ多级队列 + 动态降级 + 可调时间片综合性强,可模拟实际操作系统策略实现复杂,调参困难,开销较大操作系统核心调度机制

调度算法的比较图(逻辑角度)

    简单性:       FCFS > RR > Priority > SJF > MFQ
    公平性:       RR > MFQ > FCFS > Priority > SJF
    响应时间短:   RR ≈ MFQ > Priority > FCFS > SJF
    吞吐率高:     SJF ≈ MFQ > Priority > FCFS > RR
    实际应用性:   MFQ > RR ≈ Priority > FCFS > SJF

C++实现特性小结

算法代码复杂度用到的数据结构实现模块特点
FCFSvector + sort到达时间排序后顺序遍历
SJFvector + min_element就绪队列中每次选 burst_time 最小者
RRqueue + round robin时间片控制,剩余时间循环入队
Priorityvector + min_element每次选出优先级最小者
MFQ多个 queue多级队列+不同时间片+降级策略

调度算法设计核心要点

  1. 是否支持抢占:RR、MFQ、抢占式 Priority 是抢占式算法
  2. 是否需要 burst_time 信息:SJF 依赖精确时间
  3. 是否有优先级策略:Priority、MFQ 支持优先级
  4. 是否考虑公平性:RR、MFQ设计中具备公平性
  5. 是否容易造成饥饿:SJF、Priority 需通过策略弥补

如何选择调度算法(场景导向)

场景建议算法
后台大批量处理任务FCFS / SJF
需要交互响应迅速RR / MFQ
需要保证实时或关键任务优先Priority / MFQ
系统负载变化大,希望智能调整调度策略MFQ(+动态优先级)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值