2026 春 · 期中考试

操作系统期中
一站式复习

覆盖 Lect01-13 全部考点,按"课件知识点 → 概念辨析 → 自测巩固"递进。基于北京大学操作系统(荣誉课程)2026 春课件。

13章课件
50+知识点
30自测题
16章节

📊 考点权重(基于课件结构)整理分析

~25%

同步机制

锁、信号量、管程、条件变量、读者-写者问题、死锁检测与预防

~25%

CPU 调度

FCFS/RR/SJF/SRTF/MLFQ/EDF、Linux CFS、调度策略比较

~20%

进程与线程

线程模型、fork/exec/wait、PCB/TCB、上下文切换、进程 vs 线程

~30%

抽象 & 概念

文件抽象、管道/Socket、IPC、地址空间、虚拟内存、OS 四大概念

💡 推荐学习路径:按左侧导航顺序学习;重点掌握同步机制(锁/信号量/管程/读者写者)和调度策略(FCFS/RR/SJF/MLFQ/EDF),完成互动自测检验掌握程度。

🗺️ 考点路线图 整理导图

1

OS 概述与四大概念(Lect01-02)

OS 定义(Referee/Illusionist/Glue)、Thread/Address Space/Process/Dual Mode

2

线程与进程(Lect03)

pthreads、fork/exec/wait、线程状态、并发 vs 并行、竞态条件、临界区

3

文件与 I/O(Lect04-05)

文件描述符、FILE* 流、管道、Socket、客户端-服务器模式

4

同步基础(Lect06)

线程生命周期、上下文切换、调度循环、原子操作、互斥、临界区

5

锁与信号量(Lect07)

生产者-消费者问题、有界缓冲区、P/V 操作、信号量模式

6

原子指令与管程(Lect08)

test&set、compare&swap、Mesa vs Hoare 管程、条件变量

7

读者-写者问题(Lect09)

Monitor 解法、状态变量、优先级策略、单/多条件变量

8

经典调度(Lect10)

FCFS/RR/Priority/SJF/SRTF、CPU Burst 模型、调度目标

9

高级调度(Lect11)

Lottery/MLFQ/EDF/Linux CFS、饥饿/优先级反转/优先级捐赠

10

死锁(Lect12)

四大条件、资源分配图、检测算法、Banker's 算法、预防/恢复

第 1 章 · 操作系统概述(Lect01)

1.1 什么是操作系统?

操作系统是介于应用软件与硬件之间的特殊软件层,提供:

  • 方便的抽象:隐藏复杂硬件设备细节
  • 受保护的资源访问:管理共享资源的隔离与分配
  • 安全与认证:控制谁可以访问什么
  • 通信机制:逻辑实体间的通信

1.2 OS 的三个角色

角色职责例子
Referee(裁判)管理保护、隔离和资源共享资源分配、通信
Illusionist(魔术师)提供干净、易用的物理资源抽象无限内存、专用机器假象、虚拟化
Glue(胶水)提供通用服务存储、窗口系统、网络、共享、授权
关键理解:OS 不仅是资源管理器,更是提供抽象保护的中间层。

1.3 OS 历史简谱

  • Multics → AT&T Unix → BSD Unix → Ultrix, SunOS, NetBSD, FreeBSD
  • Mach (微内核) + Unix BSD → NextStep → XNU → macOS / iOS
  • MINIX → Linux → Android, Chrome OS, Ubuntu, Fedora, Debian
  • CP/M → QDOS → MS-DOS → Windows 3.1 → NT → ... → Windows 10/11

第 2 章 · OS 四大核心概念(Lect02)

2.1 四个基本概念

概念定义关键内容
Thread(线程)单一执行上下文,完全描述程序状态PC、寄存器、执行标志、栈
Address Space(地址空间)程序在独立于物理内存的地址空间中执行地址翻译、虚拟内存
Process(进程)执行中程序的实例,包含地址空间 + 一个或多个线程隔离、保护
Dual Mode(双模式)只有"系统"能访问某些资源用户态 vs 内核态、保护

2.2 Base and Bound(基址-界限保护)

简单的地址翻译机制:

  • 物理地址 = 虚拟地址 + Base(基址寄存器)
  • 如果虚拟地址 ≥ Bound(界限寄存器)→ 越界异常
  • 保护 OS 和进程间隔离,不需要重定位加载器
OS 核心任务 = 运行程序:源码 → 编译 → 可执行文件 → 加载到内存(代码/数据/堆/栈)→ 执行。

第 3 章 · 线程与进程(Lect03)

3.1 线程(Thread)

  • 定义:OS 提供的并发单元,每个线程代表一个任务
  • 动机:处理多任务、网络服务器、并行程序、UI 响应、隐藏 I/O 延迟
  • 三状态:RUNNING(运行中)、READY(就绪)、BLOCKED(阻塞等待 I/O)
  • 共享状态:内存内容(全局变量、堆)、I/O 状态(文件描述符、网络连接)
  • 私有状态:TCB(线程控制块)、CPU 寄存器(含 PC)、执行栈

3.2 并发 vs 并行

Concurrency(并发):同时处理多件事(handling multiple things at once)
Parallelism(并行):同时做多件事(doing multiple things simultaneously)
单核上两个线程并发执行但不并行;多核上可以既并发又并行。

3.3 多处理 vs 多道程序

  • Multiprocessing:多个 CPU/核
  • Multiprogramming:多个作业/进程
  • Multithreading:多个线程/进程

3.4 pthreads API

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                   void *(*start_routine)(void*), void *arg);
void pthread_exit(void *value_ptr);
int pthread_join(pthread_t thread, void **value_ptr);

3.5 进程(Process)

  • 定义:具有受限权限的执行环境 = 地址空间 + 一个或多个线程
  • 拥有文件描述符、网络连接等资源
  • 进程间互相保护;OS 保护不受进程侵害
  • 在现代 OS 中,内核之外的一切都在进程中运行

3.6 进程管理 API

系统调用功能
fork()复制当前进程(返回 0=子进程,>0=父进程得到子进程 pid)
exec()替换当前进程运行的程序(不返回如果成功)
wait()等待子进程结束
exit()终止进程
kill()向另一个进程发送信号
sigaction()设置信号处理函数

3.7 fork() 关键特性

  • fork() 后父进程和子进程的地址空间(内存)、文件描述符等全部复制
  • 但两者独立运行,调度顺序不确定(竞态)
  • 不要在 fork() 后直接做复杂操作而不 exec()(多线程进程中尤其危险——其他线程会消失)

3.8 进程 vs 线程

同一进程不同进程
切换开销低 (~100 ns)高 (~3-4 μs)
保护
共享开销中/高
并行Yes(多核)Yes

3.9 竞态条件(Race Condition)

定义:多个线程并发访问共享数据,最终结果取决于线程执行的精确时序(调度顺序)。
例子:Thread A: x = y + 1; Thread B: y = 2; y = y * 2; → x 可能为 1, 3 或 5。

第 4 章 · 文件与 I/O(Lect04)

4.1 "Everything is a File" 哲学

Unix/POSIX 中,磁盘文件、设备、网络 Socket、管道等使用统一接口:open()read()write()close()

4.2 文件系统抽象

  • 文件:命名的数据集合(字节序列),含元数据(大小、修改时间、所有者)
  • 目录:包含文件和子目录的"文件夹",层次命名(路径)
  • 绝对路径(/home/ff/pkuos)vs 相对路径(./, ../, ~/

4.3 I/O 层次(自底向上)

  1. 磁盘 / Flash / 控制器 / DMA
  2. I/O 驱动(命令与数据传输)
  3. 文件系统(文件/目录/索引)
  4. Open File Description(syscall 层)
  5. 文件描述符(低层 I/O)
  6. Streams(高层 I/O, FILE*

4.4 文件描述符(Low-Level I/O)

函数功能
int open(path, flags, mode)打开文件,返回文件描述符
ssize_t read(fd, buf, max)读取最多 max 字节(0=EOF, -1=错误)
ssize_t write(fd, buf, size)写入 size 字节
off_t lseek(fd, offset, whence)移动文件偏移(SEEK_SET/END/CUR)
int close(fd)关闭文件描述符

标准描述符:STDIN_FILENO (0)、STDOUT_FILENO (1)、STDERR_FILENO (2)

4.5 FILE* 流(High-Level I/O)

  • FILE* 结构包含:文件描述符 + 用户态缓冲区 + 锁
  • fopen/fclosefread/fwritefprintf/fscanffseek/ftell
  • 三个预定义流:stdinstdoutstderr
  • 缓冲原因:系统调用比函数调用昂贵得多;内核不理解格式化

4.6 fork() 与文件描述符

关键:fork() 后文件描述符被复制,但 Open File Description(含文件偏移)被共享
父进程 read(3, buf, 100) 后子进程 read(3, buf, 100),两者推进同一个文件偏移。

第 5 章 · IPC、管道与 Socket(Lect05)

5.1 进程间通信(IPC)

进程间天然隔离;通信需要在保护墙上"打洞"。两种方式:

  • 文件:持久存储,但昂贵(需写磁盘)
  • 内核内存队列:通过系统调用实现的管道 / Socket

5.2 Unix 管道(Pipe)

  • int pipe(int fileds[2]):fileds[0] 读端,fileds[1] 写端
  • 实现为固定大小队列(有界缓冲区)
  • 写满则阻塞(生产者睡眠),读空则阻塞(消费者睡眠)
  • 单向、单机、需通过 fork() 继承文件描述符

5.3 Socket(套接字)

  • 通信端点,外观类似文件 I/O(有文件描述符)
  • 双向(两个队列,每个方向一个)
  • 进程可在不同机器上,无需共同祖先
  • 同一抽象适用于本地、TCP/IP、UDP/IP

5.4 管道 vs Socket

管道Socket
方向单向(单队列)双向(双队列)
范围仅同机同机或跨机
建立方式pipe() → fork() 继承socket/bind/connect/listen/accept
接口read/writeread/write(有些操作不支持如 lseek)

5.5 Socket 客户端-服务器模式

服务器端:socket() → bind() → listen() → accept() → read/write → close()

客户端:socket() → connect() → write/read → close()

TCP 连接由 5 元组标识:(源IP, 源端口, 目的IP, 目的端口, 协议)

5.6 并发服务器设计模式

模式特点
v1 Sequential单进程,一次处理一个连接
v2 fork (protection)每连接 fork,父进程 wait 子进程
v3 fork (concurrent)每连接 fork,父进程不 wait(并发)
v4 threads每连接新线程(效率高,保护弱)
Thread Pool固定大小线程池,master 分发连接
自测
关于 Unix 管道,以下说法正确的是?
A. 管道是单向通信,数据流只能从写端到读端
B. 管道可以在不同机器的进程间通信
C. 管道的读写两端可以使用同一个文件描述符
D. 管道缓冲区满时,write() 返回错误
答案:A
管道是单向的(fileds[1] 写 → fileds[0] 读),仅限同机,缓冲区满时 write() 阻塞(不返回错误)。

第 6 章 · 并发与同步基础(Lect06)

6.1 进程控制块(PCB)

  • 内核中每个进程/线程的表示结构
  • 包含:状态(running/ready/blocked)、寄存器状态、PID、优先级、执行时间、内存空间等

6.2 线程/进程生命周期

状态转换:newreadyrunningwaiting(等待事件)→ ready → ... → terminated

6.3 上下文切换(Context Switch)

步骤:保存当前线程状态(PC、寄存器、栈指针)到 TCB → 加载新线程状态从 TCB 到 CPU。
关键:忘记保存/恢复任何一个寄存器 → 间歇性故障。

6.4 调度循环(Dispatch Loop)

Loop {
    RunThread();
    ChooseNextThread();     // 调度策略
    SaveStateOfCPU(curTCB); // 保存上下文
    LoadStateOfCPU(newTCB); // 恢复上下文
}

6.5 内部事件 vs 外部事件

内部事件外部事件
触发线程自愿放弃 CPU(I/O 阻塞、yield)中断(硬件/软件信号)
典型等待 I/O、等待信号定时器中断、设备中断
作用线程主动让出确保调度器能重新获得控制权

6.6 同步核心定义

术语定义
原子操作要么完全执行,要么完全不执行;不可分割
同步使用原子操作确保线程间的协调
互斥确保同一时间只有一个线程做某件事
临界区一次只能有一个线程执行的代码段
一次只能由一个线程持有的对象,提供互斥
并发很难!线程程序必须对所有可能的指令交织都正确。
历史教训:Therac-25(辐射过量)、Mars Pathfinder(优先级反转)、Toyota(失控加速)。

第 7 章 · 锁与信号量(Lect07)

7.1 锁的基本操作

  • Lock.acquire() — 等待直到锁空闲,然后标记为忙
  • Lock.release() — 标记锁为空闲
  • 只有持有锁的线程才能释放锁

7.2 信号量(Semaphore)

定义(Dijkstra, 1960s):非负整数值,支持两个原子操作:
P() / down() / wait():等待信号量 > 0,然后减 1
V() / up() / signal():加 1,唤醒一个等待的 P(如果有)
"P" = "proberen"(测试),"V" = "verhogen"(增加),荷兰语。

7.3 信号量的两种模式

模式初值用法
互斥(Mutex)1P(mutex); 临界区; V(mutex)
信号/同步0等待者 P(sem); 通知者 V(sem)

7.4 生产者-消费者(有界缓冲区)

Semaphore fullSlots = 0;
Semaphore emptySlots = bufSize;
Semaphore mutex = 1;

Producer(item) {
    P(&emptySlots);    // 等待空位
    P(&mutex);         // 获取互斥
    Enqueue(item);     // 临界区
    V(&mutex);
    V(&fullSlots);     // 通知消费者
}

Consumer() {
    P(&fullSlots);     // 等待数据
    P(&mutex);         // 获取互斥
    item = Dequeue();  // 临界区
    V(&mutex);
    V(&emptySlots);    // 通知生产者
    return item;
}
关键规则:
• P 操作的顺序很重要(先 P(mutex) 再 P(fullSlots) 会死锁!)
• V 操作的顺序不重要(仅影响调度效率)
• 为每个约束使用独立的信号量
• 不对称性:生产者 P(emptySlots)/V(fullSlots),消费者 P(fullSlots)/V(emptySlots)

第 8 章 · 原子指令与管程(Lect08)

8.1 锁的实现:禁用中断

简单但有问题:不能让用户态禁用中断;实时性无保证;临界区可能很长导致错过 I/O 事件。

8.2 原子 Read-Modify-Write 指令

指令行为体系结构
test&set(&addr)返回旧值,设置 addr = 1多数体系
swap(&addr, reg)交换寄存器与内存值x86
compare&swap(&addr, r1, r2)若 *addr == r1,则 *addr = r268000 等

硬件负责在单处理器和多处理器上的正确性。

8.3 基于 test&set 的简单锁(忙等)

int value = 0;
Acquire() { while (test&set(value)); }  // 忙等
Release() { value = 0; }

问题:忙等浪费 CPU;可能导致优先级反转;多处理器上引起缓存乒乓效应。

8.4 管程(Monitor)

定义:一把锁 + 零个或多个条件变量,用于管理对共享数据的并发访问。
锁:提供对共享数据的互斥访问。
条件变量:临界区内等待某条件的线程队列。
关键思想:允许在临界区内睡眠——原子性地释放锁并进入睡眠。

8.5 条件变量操作

  • Wait(&lock):原子性地释放锁并睡眠;返回前重新获取锁
  • Signal():唤醒一个等待者(如果有)
  • Broadcast():唤醒所有等待者
  • 规则:进行条件变量操作时必须持有锁

8.6 Mesa vs Hoare 管程

HoareMesa
Signal 后Signal 者交出锁和 CPU,等待者立即运行Signal 者保持锁和 CPU,等待者放入就绪队列
Wait 返回后条件一定成立条件可能已被改变 → 必须用 while 循环
效率强制上下文切换(低效)更高效、更容易实现
使用多数教科书多数实际 OS

8.7 信号量 vs 条件变量

信号量有记忆(V 操作增加计数,后续 P 可以匹配);
条件变量没有记忆(如果没有等待者,Signal 丢失)。

第 9 章 · 读者-写者问题(Lect09)

9.1 问题定义

共享数据库,两类用户:

  • 读者(Readers):只读,不修改。允许多个读者同时访问。
  • 写者(Writers):读写。同一时间只能有一个写者,且不能有读者。

9.2 正确性约束

  1. 没有写者时,读者可以访问数据库
  2. 没有读者或写者时,写者可以访问数据库
  3. 同一时间只有一个线程操作状态变量

9.3 状态变量

变量含义初始值
AR活跃读者数0
WR等待读者数0
AW活跃写者数0
WW等待写者数0

两个条件变量:okToReadokToWrite。所有状态变量由一个锁保护。

9.4 读者代码

Reader() {
    acquire(&lock);
    while ((AW + WW) > 0) {     // 有写者存在 → 等待
        WR++;
        cond_wait(&okToRead, &lock);
        WR--;
    }
    AR++;
    release(&lock);
    AccessDatabase(ReadOnly);    // 实际读操作
    acquire(&lock);
    AR--;
    if (AR == 0 && WW > 0)      // 最后一个读者,唤醒写者
        cond_signal(&okToWrite);
    release(&lock);
}

9.5 写者代码

Writer() {
    acquire(&lock);
    while ((AW + AR) > 0) {     // 有活跃用户 → 等待
        WW++;
        cond_wait(&okToWrite, &lock);
        WW--;
    }
    AW++;
    release(&lock);
    AccessDatabase(ReadWrite);   // 实际写操作
    acquire(&lock);
    AW--;
    if (WW > 0)                 // 优先给写者
        cond_signal(&okToWrite);
    else if (WR > 0)            // 否则唤醒所有读者
        cond_broadcast(&okToRead);
    release(&lock);
}
写者优先策略:Writer 退出时先检查 WW > 0(唤醒写者),再检查 WR > 0(唤醒读者)。
读者可能饥饿——如果写者持续到来。
如果只有一个条件变量——必须用 broadcast() 而非 signal()。

第 10 章 · 经典调度策略(Lect10)

10.1 CPU Burst 模型

程序在 CPU Burst(使用 CPU)和 I/O Burst(等待 I/O)之间交替。CPU Burst 倾向于小 Burst。每次调度决策是关于下一个 CPU Burst给哪个作业。

10.2 调度目标

目标含义
最小化完成时间操作/作业的经过时间(用户感知)
最大化吞吐量每秒操作/作业数(最小化开销,高效资源利用)
公平性用户间公平共享 CPU

10.3 经典调度策略对比

策略特点优点缺点
FCFS/FIFO先来先服务,运行直到阻塞简单短作业被长作业阻塞(队头阻塞)
Round Robin (RR)时间片轮转,q 时间后抢占公平、短作业友好上下文切换开销
Strict Priority总是运行最高优先级保证重要任务低优先级可能饥饿
SJF(非抢占)运行最短作业最优平均完成时间需预测未来、不公平
SRTF(抢占版 SJF)运行剩余时间最短的抢占中最优平均完成时间需预测、长作业饥饿

10.4 Round Robin 关键参数

  • 时间片 q:q 太大 → 接近 FCFS;q 太小 → 上下文切换开销主导
  • n 个进程,最大等待 ≤ (n-1)q
  • 典型值:10-100ms

10.5 SRTF 是最优的

SJF 是非抢占中可证明最优的平均完成时间调度。
SRTF 是抢占中可证明最优的平均完成时间调度。
预测:τn = α·tn-1 + (1-α)·τn-1(指数平均)

第 11 章 · 高级调度(Lect11)

11.1 Lottery Scheduling(彩票调度)

  • 给每个作业分配彩票票数;每次时间片随机抽取中奖票
  • 平均 CPU 时间与票数成正比
  • 近似 SRTF:短作业多给票;每个作业至少 1 票(无饥饿)
  • 优点:负载变化时优雅地调整比例

11.2 Multi-Level Feedback Queue (MLFQ)

  • 多队列,不同优先级;高优先级 = "前台"任务
  • 每队列有独立调度算法(前台 RR,后台 FCFS)
  • 量子指数增长(1ms, 2ms, 4ms, ...)
  • 调整规则:作业从最高优先级开始;用完时间片 → 降级;主动放弃 CPU(I/O) → 升回
  • 结果:近似 SRTF——CPU 密集型作业快速降级,I/O 密集型作业留在顶部

11.3 Linux CFS(完全公平调度器)

  • 追踪每个线程的 CPU 时间;选择 CPU 时间最小的线程运行
  • 近似"理想"多任务处理器(公平队列)
  • O(log N) 使用堆实现
  • 目标延迟:每个进程在此周期内获得服务(如 20ms)
  • 最小粒度:最小时间片长度(如 1ms)——防止过度上下文切换
  • 睡眠线程不推进 CPU 时间 → 醒来时获得提升(自动交互性)

11.4 EDF(最早截止时间优先)

  • 实时调度:优先级基于绝对截止时间 Dabs = Dold + Pi
  • 总是调度绝对截止时间最近的活动任务
  • 可行性测试:Σ(Ci / Di) ≤ 1

11.5 饥饿 vs 死锁

饥饿(Starvation):线程无限期无法推进(可能结束但不一定)
死锁(Deadlock):资源的循环请求(没有外部干预无法结束)
死锁 → 饥饿,但饥饿 ⇏ 死锁。

11.6 优先级反转与优先级捐赠

高优先级任务被低优先级任务阻塞(因为锁)。中等优先级任务饥饿高优先级任务。

解决:优先级捐赠/继承——高优先级线程临时将优先级授予低优先级锁持有者。

著名案例:Mars Pathfinder 因优先级反转导致系统重置,启用优先级捐赠后修复。

11.7 调度策略选择指南

目标推荐调度器
CPU 吞吐量FCFS
平均完成时间SRTF 近似(MLFQ)
I/O 吞吐量SRTF 近似
公平性(CPU 时间)Linux CFS
公平性(等待时间)Round Robin
满足截止时间EDF
偏袒重要任务Priority

第 12 章 · 死锁(Lect12)

12.1 死锁的定义

线程 A 持有资源 1,等待资源 2;线程 B 持有资源 2,等待资源 1 → 循环等待

12.2 死锁的四个必要条件

1. 互斥(Mutual Exclusion):一次只能有一个线程使用资源
2. 持有并等待(Hold and Wait):线程持有至少一个资源,同时等待其他资源
3. 不可抢占(No Preemption):资源只能被持有者自愿释放
4. 循环等待(Circular Wait):存在集合 {T1,...,Tn},Ti 等待 Ti+1 持有的资源
四个条件必须同时满足才可能死锁。

12.3 资源分配图

  • 节点:线程 (T) 和资源类型 (R, 含 Wi 个实例)
  • 请求边:Ti → Rj(线程请求资源)
  • 分配边:Rj → Ti(资源分配给线程)
  • 有环不一定死锁(如果有多实例);环是必要条件不是充分条件

12.4 死锁的四种处理方式

方式策略
预防(Prevention)打破四个条件之一。如:强制排序(总是先获取 x 再 y)、一次性请求所有资源
恢复(Recovery)让死锁发生再恢复。终止线程、抢占资源、回滚操作(数据库常用)
避免(Avoidance)动态延迟资源请求。如 Banker's 算法,保持系统在安全状态
忽略(Ostrich)假装死锁不会发生(大多数 OS 的做法)

12.5 Banker's 算法

  • 提前声明最大资源需求
  • 允许线程继续当且仅当:(available - requested) ≥ 任何线程可能需要的最大剩余量
  • 如果结果为无死锁,则批准请求;否则拒绝
  • 保持系统在安全状态

12.6 死锁检测算法

[Avail] = [FreeResources]
UNFINISHED = 所有节点
do {
    done = true
    For each node in UNFINISHED {
        if ([Request_node] <= [Avail]) {
            remove node from UNFINISHED
            [Avail] = [Avail] + [Alloc_node]
            done = false
        }
    }
} until(done)
// UNFINISHED 中剩余的节点 → 死锁

12.7 哲学家(律师)就餐问题

  • 5 根筷子、5 个哲学家,需要 2 根才能吃
  • 如果所有人同时拿左边 → 死锁
  • 修复:不允许拿最后一根筷子(除非某饥饿哲学家已有另一根)

第 13 章 · 现代系统调度(Lect13)

13.1 ZygOS(SOSP'17)— FCFS 启发

  • 问题:数据中心中微秒级 RPC 的尾延迟
  • 洞察:单队列模型因瞬态负载不平衡在 SLO 上有更好的吞吐
  • 方案:工作窃取(Work-Stealing)在数据平面中实现工作守恒
  • 结果:比 Linux 快 1.63×,99 分位延迟降低 3.68×

13.2 Shinjuku(NSDI'19)— RR 启发

  • 问题:微秒级抢占调度以实现低尾延迟
  • 方案:5μs 一次的抢占、专用调度核、硬件虚拟化支持快速抢占

13.3 Tiresias(NSDI'19)— SJF/SRTF/MLFQ 启发

  • 问题:无完整作业信息的 GPU 集群管理器
  • 方案:二维基于年龄的调度(2DAS)——按已执行的 GPU 时间优先调度最短作业
  • 结果:平均 JCT 比 YARN-CS 提升 5.5×

13.4 Dominant Resource Fairness (DRF)

  • 问题:多资源类型的公平共享(用户需求异构)
  • 主导资源:用户份额最大的资源
  • 主导份额:主导资源被分配的比例
  • 对主导份额应用 max-min 公平
  • 属性:份额保证 ✓、策略证明 ✓、帕累托效率 ✓、无嫉妒 ✓

13.5 经典与现代映射

经典策略现代系统
FCFSZygOS (SOSP'17)
RRShinjuku (NSDI'19)
SJF, SRTF, MLFQTiresias (NSDI'19)
公平性DRF (NSDI'11), FairRide (NSDI'16)
这章展示了经典 OS 调度概念(FCFS/RR/SJF/公平性)如何启发现代数据中心和集群系统的设计。

互动自测(打分)DeepSeek 生成

选择题自测

30
0 / 已答 0 · 正确率 0%

优缺点对比总结 课件整理

以下内容均来自课件中的对比讨论,帮助快速记忆各方案/机制的优劣。

🧵 进程 vs 线程

进程线程
优点 强隔离保护;崩溃不影响其他进程;独立地址空间 切换开销低 (~100ns);共享内存方便通信;创建快
缺点 切换开销高 (~3-4μs);共享开销高(IPC 需内核介入) 保护弱(一个线程崩溃可能影响整个进程);同步复杂

📡 管道 vs Socket

管道Socket
优点 简单;通过 fork 继承即可建立;无需寻址 双向通信;可跨机器;标准网络 API;灵活的 C/S 模式
缺点 单向(需两根管道才能双向);仅限同机;需共同祖先 建立过程复杂(socket/bind/listen/accept/connect);需要地址和端口管理

🔒 同步机制对比

机制优点缺点
Lock (mutex) 简单直观;互斥保证强 只能互斥;不能表达复杂条件等待
Semaphore 有记忆(V 不会丢失);可做互斥也可做信号;灵活 P 操作顺序错误易死锁;代码分散不易维护
Monitor + CV 临界区内可睡眠(原子释放锁);代码结构化;支持 broadcast CV 无记忆(Signal 无等待者则丢失);Mesa 需 while 循环重检

🏗️ 锁实现方式对比

实现优点缺点
禁用中断 简单(单处理器上) 用户态不能做;临界区长会丢 I/O;多处理器不工作
test&set 忙等 硬件保证原子性;多处理器可用 忙等浪费 CPU;缓存乒乓效应;可能导致优先级反转
test&set + 睡眠 短忙等 + 长等待时睡眠,减少 CPU 浪费 实现更复杂;需管理等待队列

🏫 Mesa vs Hoare 管程

HoareMesa
优点 Wait 返回后条件一定成立(if 即可);语义清晰 实现效率高;无需强制上下文切换;实际 OS 广泛使用
缺点 强制上下文切换低效;实现复杂 Wait 返回后条件可能已变(需 while 循环);程序员易写错

⏱️ 调度策略对比

策略优点缺点
FCFS 简单;无上下文切换开销;对全等长作业最优吞吐 队头阻塞(短作业被长作业卡住);平均等待时间差
Round Robin 公平;短作业友好;最大等待有上界 上下文切换开销;q 太小吞吐差;q 太大退化为 FCFS
SJF / SRTF 最优平均完成时间(SJF 非抢占,SRTF 抢占);I/O 吞吐好 需预测未来(不准);长作业可能饥饿;不公平
Priority 保证重要任务优先;可配合其他策略 低优先级可能饥饿;优先级反转问题
MLFQ 自适应(近似 SRTF);无需预知作业长度;兼顾交互与批处理 可被欺骗(加无意义 I/O 保持高优先级);参数调优复杂
Lottery 概率公平;负载变化自适应;无饥饿(每作业至少 1 票) 短期不公平(随机性);不适合硬实时
Linux CFS 公平(CPU 时间);自动交互性(睡眠线程获提升);O(log N) 对实时任务不够好;不保证截止时间
EDF 可证明最优满足截止时间(若 ΣC/D ≤ 1);动态适应 需预知计算时间和周期;过载时行为不可预测

💀 死锁处理方式对比

方式优点缺点
预防 (Prevention) 从根本上杜绝死锁 限制严格(强制排序等),降低并发性和资源利用率
避免 (Avoidance) 动态判断,最大限度利用资源 需预知最大需求(不现实);Banker's 算法开销大
恢复 (Recovery) 允许死锁发生再处理,前期无限制 恢复代价大(终止线程、回滚);数据可能丢失
忽略 (Ostrich) 零开销;大多数场景死锁概率低 死锁发生时系统僵死;不可用于关键系统

🖥️ 并发服务器模式对比

模式优点缺点
Sequential 简单;无同步问题 一次只能处理一个连接;其他连接等待
fork (per connection) 强隔离(进程);并发处理 fork 开销大;大量连接时进程数爆炸
Thread per connection 低开销;共享地址空间方便 保护弱;线程数过多时调度开销大
Thread Pool 控制资源使用;避免频繁创建/销毁线程 线程数固定时突发流量排队;调优池大小需经验

⚛️ 信号量 vs 条件变量

信号量条件变量
记忆性 有记忆:V 操作增加计数,后续 P 可匹配 无记忆:Signal 时无等待者则丢失
使用场景 资源计数、简单互斥、生产者-消费者 复杂条件等待(需在临界区内睡眠时)
优点 灵活;两种模式(互斥+同步) 结构清晰;配合 Monitor 使用;支持 broadcast
缺点 P 顺序错→死锁;代码分散;不易维护 必须配合锁使用;Mesa 语义下需 while 重检

📂 高层 I/O (FILE*) vs 低层 I/O (fd)

FILE* 流 (High-Level)文件描述符 (Low-Level)
优点 用户态缓冲(性能好);格式化 I/O;方便的行/块操作 无额外缓冲层;行为可预测;与 fork/dup 等配合直观
缺点 与 fd 混用会读到错误数据;需 fflush 保证持久化 逐字节读性能差;无格式化支持;需手动管理缓冲区

考前速查表 整理速查

🔑 同步机制对比

机制操作特点适用场景
Lockacquire / release互斥、一次一个线程临界区保护
SemaphoreP(down) / V(up)计数、有记忆、可作锁或信号生产者-消费者、资源计数
MonitorWait / Signal / Broadcast锁+条件变量、临界区内睡眠复杂同步(读者-写者)
Condition VarWait / Signal / Broadcast无记忆(Signal 丢失无等待者)配合 Monitor 使用

🔄 调度策略速查

策略抢占最优性饥饿风险
FCFS无限循环阻塞所有
RR无(最大等待 ≤ (n-1)q)
SJF最优平均完成时间长作业可能饥饿
SRTF抢占中最优平均完成时间长作业可能饥饿
Priority可配低优先级可能饥饿
MLFQ近似 SRTF近似 SRTF
EDF满足截止时间—(如果 ΣC/D ≤ 1)
CFS公平(CPU 时间)

⚠️ 死锁四大条件与对策

条件打破方式
互斥虚拟化资源(如假脱机打印)
持有并等待一次性请求所有资源
不可抢占允许抢占资源(回滚+重试)
循环等待强制资源排序(总是先获取 x 再 y)

📋 Mesa vs Hoare 管程

HoareMesa(实际使用)
Signal 语义Signal 者放弃 CPU,等待者立即运行Signal 者保持 CPU
Wait 返回后条件一定成立(if 检查)条件可能已变(while 检查)

📝 fork() vs exec() vs wait()

调用行为返回
fork()复制当前进程(地址空间+文件描述符)父进程:子 pid;子进程:0;出错:-1
exec()替换当前进程的程序成功不返回;失败返回 -1
wait(&status)等待子进程结束结束的子进程 pid

🧵 线程共享 vs 私有

共享(所有线程)私有(每线程)
全局变量、堆、代码TCB、寄存器(含 PC)、栈
文件描述符、网络连接线程元数据