目录
第一章 操作系统概述
1.1 操作系统的基本概念
操作系统(Operating System,OS)是计算机系统中最为核心的系统软件,它是硬件与应用程序之间的桥梁,负责管理和协调计算机系统的各种资源。从用户角度看,操作系统是一个控制计算机的程序;从系统角度看,它则是资源管理器和扩展机器。
1.1.1 操作系统的定义
操作系统可以被定义为:
-
资源管理者:有效管理计算机系统的硬件和软件资源
-
服务提供者:为用户和应用程序提供便捷的使用接口
-
系统控制者:协调和控制计算机系统中各个部件的运行
1.1.2 操作系统的特征
现代操作系统通常具备以下基本特征:
-
并发性(Concurrency):
-
指系统能够处理多个同时发生的活动
-
通过进程和线程机制实现
-
示例:用户可以边听音乐边编辑文档
-
-
共享性(Sharing):
-
系统资源可供多个并发执行的程序共同使用
-
包括两种共享方式:
-
互斥共享(如打印机)
-
同时访问(如磁盘文件)
-
-
-
虚拟性(Virtualization):
-
将物理实体映射为多个逻辑对应物
-
典型实现:
-
CPU虚拟化(分时复用)
-
内存虚拟化(虚拟地址空间)
-
-
-
异步性(Asynchronism):
-
程序的执行以不可预知的速度推进
-
操作系统必须保证在任意环境下都能获得正确结果
-
1.2 操作系统的发展历程
1.2.1 手工操作阶段(1940-1950年代)
-
特点:
-
程序员直接操作机器
-
串行处理作业
-
资源利用率极低
-
-
典型问题:
-
CPU等待手工操作
-
大量人工干预
-
1.2.2 批处理系统(1950-1960年代)
-
简单批处理:
-
引入监控程序(Monitor)
-
作业自动连续处理
-
减少人工干预
-
-
多道批处理:
-
关键技术突破:
-
中断技术
-
通道技术
-
-
主要特征:
-
内存中同时存放多道程序
-
CPU与I/O设备并行工作
-
-
1.2.3 分时系统(1960年代)
-
革命性创新:
-
时间片轮转机制
-
交互式操作界面
-
-
典型系统:
-
CTSS(兼容分时系统)
-
MULTICS
-
UNIX前身
-
1.2.4 现代操作系统(1980年代至今)
-
主要发展方向:
-
个人计算机操作系统(Windows、macOS)
-
分布式操作系统
-
实时操作系统
-
嵌入式操作系统
-
1.3 操作系统的功能组成
1.3.1 处理器管理
-
核心功能:
-
进程控制(创建/撤销/阻塞/唤醒)
-
进程同步(互斥/同步机制)
-
进程通信(共享内存/消息传递)
-
进程调度(作业/进程/线程调度)
-
1.3.2 存储器管理
-
主要任务:
-
内存分配与回收
-
地址转换(逻辑→物理)
-
内存保护(隔离不同进程)
-
虚拟内存管理
-
1.3.3 设备管理
-
关键功能:
-
设备分配
-
设备驱动
-
缓冲管理
-
虚拟设备(SPOOLing技术)
-
1.3.4 文件管理
-
核心职责:
-
文件存储空间管理
-
目录管理
-
文件读写控制
-
文件保护机制
-
1.4 主流操作系统类型
1.4.1 批处理操作系统
-
典型特征:
-
作业成批处理
-
无用户交互
-
高吞吐量
-
-
现代应用:
-
科学计算任务
-
后台数据处理
-
1.4.2 分时操作系统
-
主要特点:
-
多用户同时访问
-
交互式响应
-
公平的时间分配
-
-
代表系统:
-
UNIX/Linux
-
OpenVMS
-
1.4.3 实时操作系统
-
关键指标:
-
严格的时间约束
-
高可靠性
-
可预测性
-
-
分类:
-
硬实时(工业控制)
-
软实时(多媒体系统)
-
1.4.4 嵌入式操作系统
-
特殊要求:
-
资源受限环境
-
低功耗设计
-
实时性要求
-
-
典型代表:
-
VxWorks
-
FreeRTOS
-
华为LiteOS
-
1.5 操作系统体系结构
1.5.1 单体结构
-
特点:
-
所有功能模块在内核态运行
-
模块间直接调用
-
-
优点:
-
高性能
-
简单直接
-
-
缺点:
-
可维护性差
-
可靠性风险
-
1.5.2 层次结构
-
典型分层(自底向上):
-
硬件抽象层
-
内存管理
-
进程管理
-
I/O管理
-
用户接口
-
1.5.3 微内核结构
-
设计理念:
-
最小化内核功能
-
多数服务运行在用户态
-
-
优势:
-
高可靠性
-
易扩展性
-
-
代表系统:
-
Mach内核
-
Windows NT(部分采用)
-
1.5.4 外核结构
-
创新思想:
-
将资源保护与抽象分离
-
应用程序直接管理硬件资源
-
内核仅负责安全隔离
-
1.6 现代操作系统发展趋势
1.6.1 多核与并行计算
-
挑战:
-
并行编程模型
-
资源竞争管理
-
能耗控制
-
1.6.2 云计算与虚拟化
-
关键技术:
-
容器技术(Docker)
-
虚拟机监控器(Hypervisor)
-
弹性资源分配
-
1.6.3 安全与可信计算
-
发展方向:
-
可信执行环境(TEE)
-
硬件级安全(SGX)
-
零信任架构
-
1.6.4 物联网与边缘计算
-
特殊需求:
-
轻量级内核
-
实时响应
-
分布式协同
-
本章重点概念:操作系统定义、四大特征(并发/共享/虚拟/异步)、五大管理功能(处理机/存储/设备/文件/用户接口)、四大类型(批处理/分时/实时/嵌入式)
第二章 操作系统运行机制
2.1 操作系统的运行架构
2.1.1 双模式运行机制
-
内核态(Kernel Mode):
-
PSW寄存器状态位为1
-
可执行特权指令
-
访问所有硬件资源
-
运行操作系统内核程序
-
-
用户态(User Mode):
-
PSW寄存器状态位为0
-
仅能执行非特权指令
-
受限的资源访问
-
运行应用程序
-
2.1.2 状态切换机制
-
用户态→内核态:
-
通过中断自动触发(硬件完成PSW修改)
-
典型场景:
-
系统调用(执行陷入指令)
-
硬件中断(时钟/I/O)
-
程序异常(如除零错误)
-
-
-
内核态→用户态:
-
通过特权指令显式设置PSW
-
在中断/系统调用处理完毕后执行
-
2.2 中断处理机制
2.2.1 中断分类
中断类型 | 触发源 | 典型场景 | 处理特点 |
---|---|---|---|
内中断 | CPU内部 | ①执行特权指令(用户态) | 同步发生 |
(异常) | ②除零错误等异常 | 不可屏蔽 | |
③陷入指令(系统调用) | |||
外中断 | 外部设备 | ①时钟中断(时间片到期) | 异步发生 |
②I/O中断(设备操作完成) | 可屏蔽/非屏蔽 |
2.2.2 中断处理流程
-
中断触发:硬件检测到中断信号
-
状态保存:自动压栈保存PSW和PC值
-
模式切换:硬件将PSW置为内核态(1)
-
查向量表:根据中断类型号定位处理程序
-
执行处理:运行对应的中断服务例程
-
恢复现场:恢复PSW和PC,返回用户态
关键数据结构:
中断向量表——存储各中断处理程序入口地址的数组,下标对应中断类型号
2.3 系统调用实现
2.3.1 系统调用与库函数对比
特性 | 系统调用 | 库函数 |
---|---|---|
执行环境 | 必须在内核态执行 | 通常在用户态执行 |
实现位置 | 操作系统内核 | 语言运行时库 |
功能范围 | 资源相关操作 | 通用功能 |
调用开销 | 高(需模式切换) | 低(无状态转换) |
示例 | fork() , open() | abs() , strcpy() |
2.3.2 系统调用执行过程
应用程序->>+内核: 1. 传递系统调用参数 应用程序->>+内核: 2. 执行陷入指令(如int 0x80) 内核-->>-硬件: 3. 触发内中断(自动完成) 硬件->>+内核: 4. 保存现场并切换至内核态 内核->>内核: 5. 查系统调用表定位处理程序 内核->>内核: 6. 执行具体服务例程 内核-->>-硬件: 7. 执行特权指令恢复用户态 硬件-->>-应用程序: 8. 返回结果并继续执行
2.4 操作系统内核结构
2.4.1 大内核(宏内核)架构
-
特点:
-
所有核心功能(进程管理、内存管理等)都在内核空间运行
-
模块间通过函数调用直接通信
-
-
优势:
-
高性能(无模式切换开销)
-
功能集成度高
-
-
缺点:
-
可靠性风险(任一模块错误导致全系统崩溃)
-
可维护性差
-
-
典型代表:
-
Linux内核
-
Windows NT内核(部分模块化)
-
2.4.2 微内核架构
-
特点:
-
仅最基本功能(IPC、地址空间)在内核
-
其他服务(文件系统、驱动)运行在用户态
-
-
优势:
-
高可靠性(故障隔离)
-
易于扩展
-
-
缺点:
-
性能开销(频繁模式切换)
-
-
典型代表:
-
Mach内核(macOS基础)
-
QNX实时系统
-
2.5 系统引导流程
2.5.1 启动阶段
-
BIOS/UEFI阶段:
-
硬件自检(POST)
-
从固定地址(0xFFFF0)执行ROM引导程序
-
读取主引导记录(MBR/GPT)
-
-
引导加载阶段:
-
加载bootloader(如GRUB)
-
扫描活动分区
-
读取分区引导记录
-
-
内核初始化:
-
加载内核映像到内存
-
初始化关键数据结构(页表、中断向量表等)
-
启动init进程(PID=1)
-
2.5.2 关键组件
-
主引导记录(MBR):
-
位于磁盘第一个扇区(512字节)
-
包含:引导代码(446B)+分区表(64B)+魔数(0x55AA)
-
-
init进程:
-
所有用户进程的祖先
-
负责启动系统服务(如getty、网络守护进程)
-
第三章 进程管理
3.1 进程的基本概念
3.1.1 进程的定义与特征
进程是操作系统中最基本的执行单元,经典定义包括:
-
执行中的程序:程序的一次动态执行过程
-
资源分配单位:系统进行CPU调度和资源分配的基本实体
-
具有独立功能的程序:在某个数据集合上的一次运行活动
进程的五大特征:
-
动态性:具有生命周期(创建→运行→终止)
-
并发性:多个进程可同时存在于内存中
-
独立性:各进程拥有独立的地址空间和系统资源
-
异步性:进程以不可预知的速度推进
-
结构性:由程序段、数据段和进程控制块(PCB)组成
3.1.2 进程控制块(PCB)
PCB是操作系统管理进程的核心数据结构,包含:
struct PCB {
int pid; // 进程标识符
int state; // 进程状态(就绪/运行/阻塞等)
int priority; // 优先级
struct registers regs; // 寄存器保存区
void *mem_base; // 内存分配基址
struct list_head list; // 进程队列指针
// ...其他控制信息
};
PCB主要功能:
-
唯一标识进程(通过PID)
-
保存进程运行现场(寄存器值)
-
记录资源分配情况
-
实现进程调度(状态/优先级)
3.2 进程的状态与转换
3.2.1 五状态模型
状态 | 描述 | 转换条件 |
---|---|---|
新建态 | 进程正在被创建 | 分配PCB→就绪态 |
就绪态 | 已获得除CPU外的所有资源 | 被调度程序选中→运行态 |
运行态 | 正在CPU上执行 | 时间片用完→就绪态 等待事件→阻塞态 正常结束→终止态 |
阻塞态 | 因等待某事件(如I/O完成)而暂停执行 | 等待的事件发生→就绪态 |
终止态 | 进程执行完毕或被强制终止 | 资源回收,PCB撤销 |
3.2.2 状态转换图
3.3 进程控制
3.3.1 进程创建
创建事件:
-
用户登录(终端连接)
-
作业调度(批处理系统)
-
用户请求(shell命令或GUI操作)
-
服务调用(系统进程创建)
创建过程(UNIX fork()示例):
-
分配唯一PID
-
复制父进程PCB
-
分配资源(内存、文件等)
-
初始化子进程特定字段
-
插入就绪队列
3.3.2 进程终止
终止方式:
-
正常结束(exit()系统调用)
-
异常结束(段错误、除零等)
-
外界干预(kill信号)
终止处理:
-
关闭所有打开文件
-
释放内存资源
-
通知父进程(通过SIGCHLD)
-
撤销PCB
3.4 进程同步
3.4.1 临界区问题
临界资源:一次仅允许一个进程使用的资源(如打印机)
临界区:访问临界资源的代码段
解决方案要求:
-
互斥进入(Mutual Exclusion)
-
有空让进(Progress)
-
有限等待(Bounded Waiting)
3.4.2 信号量机制
整型信号量(存在忙等问题):
void wait(int *S) {
while (*S <= 0); // 忙等
(*S)--;
}
void signal(int *S) {
(*S)++;
}
记录型信号量(标准实现):
typedef struct {
int value;
struct process *queue;
} semaphore;
void P(semaphore *S) {
S->value--;
if (S->value < 0) {
block(S->queue); // 自我阻塞
}
}
void V(semaphore *S) {
S->value++;
if (S->value <= 0) {
wakeup(S->queue); // 唤醒等待进程
}
}
3.5 经典同步问题
3.5.1 生产者-消费者问题
问题描述:
-
生产者向缓冲区放入产品
-
消费者从缓冲区取出产品
-
缓冲区大小为N(有限资源)
信号量解法:
semaphore mutex = 1; // 缓冲区互斥
semaphore empty = N; // 空闲缓冲区数
semaphore full = 0; // 已用缓冲区数
void producer() {
while (1) {
item = produce_item();
P(&empty);
P(&mutex);
insert_item(item);
V(&mutex);
V(&full);
}
}
void consumer() {
while (1) {
P(&full);
P(&mutex);
item = remove_item();
V(&mutex);
V(&empty);
consume_item(item);
}
}
3.5.2 读者-写者问题
问题变种:
-
读者优先(可能导致写者饥饿)
-
写者优先(增加读写公平性)
读者优先解法:
semaphore rw_mutex = 1; // 读写互斥
semaphore mutex = 1; // read_count保护
int read_count = 0;
void reader() {
while (1) {
P(&mutex);
read_count++;
if (read_count == 1) P(&rw_mutex);
V(&mutex);
// 执行读操作
P(&mutex);
read_count--;
if (read_count == 0) V(&rw_mutex);
V(&mutex);
}
}
void writer() {
while (1) {
P(&rw_mutex);
// 执行写操作
V(&rw_mutex);
}
}
3.6 进程通信
3.6.1 共享内存
实现步骤:
-
创建共享内存区(shmget)
-
附加到进程地址空间(shmat)
-
直接读写内存访问
-
分离共享内存(shmdt)
特点:
-
最快的IPC方式
-
需要自行处理同步问题
3.6.2 消息传递
直接通信:
send(PID, message); // 指定目标进程
receive(PID, &message);
间接通信(通过信箱):
send(mailbox, message);
receive(mailbox, &message);
3.7 线程概念
3.7.1 线程与进程对比
特性 | 进程 | 线程 |
---|---|---|
资源拥有 | 独立地址空间和资源 | 共享进程资源 |
切换开销 | 大(需保存完整上下文) | 小(仅保存寄存器) |
创建速度 | 慢 | 快 |
通信方式 | IPC(管道、消息队列等) | 直接读写共享变量 |
3.7.2 线程实现模型
用户级线程(ULT):
-
优点:切换无需内核介入
-
缺点:一个线程阻塞导致整个进程阻塞
内核级线程(KLT):
-
优点:内核感知线程,可并行执行
-
缺点:切换需要陷入内核
混合模型:
-
多对多映射(n用户线程→m内核线程)
-
兼顾灵活性与效率
第四章 处理机调度与死锁
4.1 处理机调度层次
4.1.1 三级调度体系
调度层次 | 触发频率 | 操作对象 | 主要功能 | 状态影响 |
---|---|---|---|---|
高级调度 (作业调度) | 分钟级 | 作业 | 从外存后备队列选择作业调入内存并创建进程 | 无→创建态→就绪态 |
中级调度 (内存调度) | 秒级 | 进程 | 决定哪些挂起进程重新调入内存(对应七状态模型中的"挂起态"处理) | 挂起态↔就绪态/阻塞态 |
低级调度 (进程调度) | 毫秒级 | 进程/线程 | 从就绪队列选择进程分配CPU,是操作系统最基本的功能 | 就绪态↔运行态 |
关键细节:
-
中级调度是平衡系统负载的重要手段,当内存紧张时将进程数据调至外存(挂起),空闲时再调回
-
七状态模型在五状态基础上增加"就绪挂起"和"阻塞挂起"状态(PDF第22页图示)
4.2 调度算法
4.2.1 经典调度算法对比
算法 | 规则描述 | 可抢占性 | 优点 | 缺点 | 是否导致饥饿 |
---|---|---|---|---|---|
FCFS | 按到达顺序服务 | 不可抢占 | 实现简单 | 对短作业不利 | 否 |
SJF | 选择预估运行时间最短的作业 | 可抢占 | 平均等待时间最短 | 长作业可能饥饿 | 是 |
HRRN | 选择响应比最高的作业 (响应比=1+等待时间/预估运行时间) | 不可抢占 | 兼顾长短作业 | 需要预估运行时间 | 否 |
RR | 按时间片轮转 | 抢占式 | 公平,响应快 | 频繁切换开销大 | 否 |
优先级 | 选择优先级最高的进程 | 可配置 | 区分任务紧急程度 | 低优先级进程可能饥饿 | 是 |
多级反馈队列 | 设置多级队列,时间片递增 | 抢占式 | 综合性能最优 | 参数配置复杂 | 可能 |
重点算法示例:
-
时间片轮转
-
时间片=2时进程执行顺序:P1→P2→P1→P3→P2→P4→P4
-
时间片=5时退化为FCFS
-
-
多级反馈队列:
# 三级队列配置示例 queues = [ {'time_slice': 2, 'priority': 'HIGH'}, # 队列1 {'time_slice': 4, 'priority': 'MEDIUM'}, # 队列2 {'time_slice': 'FCFS', 'priority': 'LOW'} # 队列3 ]
4.2.2 调度性能指标
指标 | 计算公式 | 意义说明 |
---|---|---|
CPU利用率 | CPU有效工作时间 / 总时间 | 衡量CPU忙碌程度 |
系统吞吐量 | 完成作业数 / 总时间 | 单位时间内完成的工作量 |
周转时间 | 作业完成时间 - 提交时间 | 作业从提交到完成的总时间 |
带权周转时间 | 周转时间 / 实际运行时间 | 反映作业的相对等待时间 |
响应时间 | 首次响应时间 - 提交时间 | 交互式系统的关键指标 |
4.3 死锁原理
4.3.1 死锁定义与条件
死锁定义:多个进程因竞争资源而造成的一种互相等待的现象,若无外力作用,这些进程都将无法向前推进。
四个必要条件:
-
互斥条件:资源一次只能由一个进程占用
-
请求与保持:进程持有资源的同时请求新资源
-
不可抢占:已分配资源不能被强制剥夺
-
循环等待:存在进程-资源的循环等待链
4.3.2 死锁处理策略
策略 | 实现方法 | 优点 | 缺点 |
---|---|---|---|
预防 | 破坏四个必要条件中的至少一个 | 彻底避免死锁 | 资源利用率降低 |
避免 | 银行家算法:仅当系统处于安全状态时才分配资源 | 允许并发最大化 | 需预知最大资源需求 |
检测 | 定期构建资源分配图,检测环路 | 不影响正常执行 | 检测开销大 |
解除 | 1. 资源剥夺 2. 进程回退 3. 进程终止 | 快速恢复系统 | 可能造成工作丢失 |
银行家算法示例:
# 系统资源总量
Available = [3, 3, 2]
# 各进程最大需求
Max = [
[7, 5, 3], # P0
[3, 2, 2], # P1
[9, 0, 2], # P2
[2, 2, 2], # P3
[4, 3, 3] # P4
]
# 已分配资源
Allocation = [
[0, 1, 0], # P0
[2, 0, 0], # P1
[3, 0, 2], # P2
[2, 1, 1], # P3
[0, 0, 2] # P4
]
# 计算Need矩阵
Need = [[Max[i][j] - Allocation[i][j] for j in range(3)] for i in range(5)]
4.4 死锁检测与恢复
4.4.1 资源分配图
图元素:
-
圆形节点:表示进程
-
方形节点:表示资源类(其中的点表示实例)
-
请求边:P → R(进程请求资源)
-
分配边:R → P(资源已分配给进程)
检测算法:
-
找出非阻塞进程(其请求可被满足)
-
模拟资源回收,移除此进程的所有边
-
重复上述步骤,若最终图中存在边则检测到死锁
4.4.2 恢复方法
进程终止策略:
-
优先级最低优先:牺牲对系统影响最小的进程
-
进度最慢优先:终止已完成工作量最少的进程
-
资源消耗最多优先:释放最多资源的进程
-
级联终止:终止整个进程组
资源剥夺策略:
-
选择被抢占进程的标准:
-
已持有资源数量
-
剩余执行时间预估
-
优先级因素
-
4.5 综合案例分析
4.5.1 调度算法应用题
给定进程信息:
进程 | 到达时间 | 运行时间 |
---|---|---|
P1 | 0 | 7 |
P2 | 2 | 4 |
P3 | 4 | 1 |
P4 | 5 | 4 |
SJF调度结果:
时间轴:0-7(P1), 7-8(P3), 8-12(P2), 12-16(P4) 平均周转时间 = [(7-0)+(12-2)+(8-4)+(16-5)]/4 = 8.5
4.5.2 死锁避免题
假设系统当前资源分配如下:
进程 | Allocation | Need |
---|---|---|
P0 | 0 1 0 | 7 4 3 |
P1 | 2 0 0 | 1 2 2 |
Available = [3 3 2]
安全序列验证:
-
检查Need <= Available的进程(P1满足)
-
假设P1执行完成,释放资源:Available = [5 3 2]
-
接着可选择P0执行,最终得到安全序列<P1, P0>
第五章 存储器管理
5.1 存储器管理基础
5.1.1 存储体系层次
CPU寄存器->高速缓存->主存储器->磁盘缓存->固定磁盘->可移动存储
关键特性对比:
存储层级 | 访问时间 | 容量 | 成本/位 | 管理主体 |
---|---|---|---|---|
寄存器 | 1ns | <1KB | 最高 | 编译器 |
高速缓存 | 2-10ns | 1-8MB | 高 | 硬件 |
主存 | 10-100ns | 4-256GB | 中 | 操作系统 |
磁盘 | 5-20ms | 1-100TB | 低 | 操作系统/应用 |
5.1.2 地址绑定时机
-
编译时绑定:(绝对装入)
-
生成绝对代码
-
必须装入指定内存位置
-
早期DOS程序常用方式
-
-
加载时绑定:(可重定位装入)
-
生成可重定位代码
-
装入时确定实际地址
-
需硬件重定位寄存器支持
-
-
运行时绑定:(动态重定位装入)
-
执行期间可改变物理地址
-
需要MMU(内存管理单元)硬件
-
现代操作系统通用方案
-
5.2 连续内存分配
5.2.1 分配策略对比
策略 | 分区方式 | 碎片问题 | 实现复杂度 | 适用场景 |
---|---|---|---|---|
单一连续 | 整个用户区独占 | 内部碎片 | 简单 | 单道批处理系统 |
固定分区 | 预先划分等/不等大小分区 | 内部碎片 | 中等 | 早期多道系统 |
动态分区 | 按需划分 | 外部碎片 | 复杂 | 通用系统 |
动态分区分配算法:
首次适应算法:每次都从低地址开始查找,找到第一个能满足大小的空闲分区
实现:空闲分区以地址递增的次序链接,顺序查找空闲分区链或分区表
循环首次适应算法:不从链首开始查找,而是从上次找到的空闲分区的下一个空闲分区开始查找
实现:空闲分区以地址递增的次序链接(循环链表),顺序查找空闲分区链或分区表
最佳适应算法:尽可能留下大片的空闲区,优先使用更小的空闲区
实现:空闲分区以容量递增次序链接,顺序查找空闲分区链或分区表。分配后要重新连接分区链,保持容量递增
最坏适应算法:优先使用最大的连续空闲区
实现:空闲分区以容量递减次序链接,顺序查找空闲分区链或分区表。分配后要重新连接分区链,保持容量递减
5.2.2 碎片处理
-
内部碎片:已分配分区中未被利用的部分(固定分区必然存在)
-
外部碎片:分散的小空闲区无法满足需求(动态分区产生)
-
解决方案:
-
紧凑技术(Compaction):动态重定位运行中的程序
-
分页技术:彻底消除外部碎片
-
5.3 分页存储管理
5.3.1 基本分页机制
地址转换流程:
逻辑地址 → 页号(p) + 页内偏移(d) ↓ 查页表(PMT) ↓ 物理地址 → 页框号(f) + 页内偏移(d)
页表项结构:
位域 | 作用 |
---|---|
0-11 | 页框号(4KB页对应12位) |
12 | 存在位(P) |
13 | 修改位(M) |
14 | 访问位(A) |
15 | 保护位(R/W) |
5.3.2 快表(TLB)优化
-
TLB命中率公式:
有效访问时间 = TLB访问时间 + (1-命中率)×内存访问时间
-
示例计算:
假设TLB访问10ns,内存访问100ns,命中率90%: 有效时间 = 10 + (1-0.9)×(100+100) = 30ns (未使用TLB时为200ns)
5.4 分段存储管理
5.4.1 分段vs分页
特性 | 分页系统 | 分段系统 |
---|---|---|
划分单位 | 固定大小页(物理) | 逻辑段(程序结构) |
地址空间 | 一维线性 | 二维(段号+段内偏移) |
碎片 | 无外部碎片 | 有外部碎片 |
共享 | 按页共享 | 按段共享 |
保护 | 页级保护 | 段级保护(更精细) |
5.4.2 段表结构
struct segment_descriptor {
uint32_t base_addr; // 段基址
uint32_t limit; // 段长度
uint8_t present:1; // 存在位
uint8_t read_write:1; // 读写权限
uint8_t user_super:1; // 用户/内核段
// 其他控制位...
};
5.5 虚拟内存技术
5.5.1 请求分页机制
页表扩展字段:
-
状态位(P):是否在内存中
-
访问字段(A):记录访问次数/时间
-
修改位(D):页面是否被修改
-
外存地址:页面在外存的位置
缺页中断流程:
-
查页表发现P=0
-
触发缺页中断(内中断)
-
操作系统选择牺牲页
-
若D=1则写回磁盘
-
调入目标页,更新页表
-
重新执行引发中断的指令
5.5.2 页面置换算法
算法对比表:
算法 | 规则描述 | 实现复杂度 | 适用场景 |
---|---|---|---|
OPT | 淘汰将来最久不使用的页 | 不可实现 | 理论基准 |
FIFO | 淘汰最先进入的页 | 简单 | 简单嵌入式系统 |
LRU | 淘汰最近最久未使用的页 | 较高 | 通用系统 |
CLOCK | 循环扫描,淘汰访问位=0的页 | 中等 | 实际系统常用 |
改进CLOCK | 同时考虑访问位和修改位 | 较高 | 要求高的系统 |
Belady异常:
-
现象:分配更多物理块反而导致缺页增加
-
仅FIFO算法会出现
5.6 内存映射文件
5.6.1 传统文件访问vs内存映射
5.6.2 共享内存实现
-
进程A调用
shmget()
创建共享区 -
进程B通过相同key调用
shmget()
获取标识符 -
双方进程
shmat()
将共享区映射到各自地址空间 -
通过直接内存访问实现通信
5.7 存储保护机制
5.7.1 硬件支持
-
基址-界限寄存器:
-
基址寄存器:存储进程起始物理地址
-
界限寄存器:存储进程最大长度
-
每次访问检查:
物理地址 = 逻辑地址 + 基址
,且逻辑地址 < 界限
-
-
MMU功能:
-
地址转换
-
访问权限检查
-
TLB管理
-
5.7.2 典型保护违规
-
越界访问:地址 ≥ 界限寄存器值
-
权限违规:写只读页/执行非代码页
-
非法操作:用户态执行特权指令
第六章 文件管理
6.1 文件系统基础
6.1.1 文件概念与属性
文件定义:存储在外部存储设备上的具有符号名的相关数据集合
文件属性:
-
基本属性:
struct file_attr { char name[256]; // 文件名 size_t size; // 文件大小 time_t create_time;// 创建时间 time_t modify_time;// 修改时间 mode_t permission; // 访问权限(rwx) uid_t owner; // 所属用户 gid_t group; // 所属组 };
-
扩展属性:
-
隐藏标志(Windows的hidden属性)
-
系统标志(Linux的immutable属性)
-
校验和(完整性验证)
-
6.1.2 文件类型
类型 | 扩展名示例 | 特点 |
---|---|---|
普通文件 | .txt, .jpg | 包含用户数据 |
目录文件 | (无) | 组织其他文件的特殊文件 |
字符设备 | /dev/ttyS0 | 按字符流访问的设备 |
块设备 | /dev/sda | 按数据块访问的设备 |
符号链接 | (ln -s创建) | 包含指向另一文件路径的指针 |
管道文件 | (mkfifo创建) | 进程间通信的先进先出队列 |
6.2 文件逻辑结构
6.2.1 无结构文件
-
特点:字节流形式,无内部结构
-
典型应用:
-
文本文件(源代码、配置文件)
-
二进制可执行文件
-
-
访问方式:
# 顺序访问示例 with open('data.bin', 'rb') as f: while chunk := f.read(1024): # 每次读取1KB process(chunk)
6.2.2 有结构文件
(1) 顺序文件
-
定长记录:
struct employee { int id; // 4字节 char name[20]; // 20字节 float salary; // 4字节 }; // 每条记录固定28字节
-
随机访问公式:
第i条记录位置 = i * record_size
-
-
变长记录:
-
需要分隔符或长度前缀
-
访问必须顺序进行(无法直接定位)
-
(2) 索引文件
二级索引结构:
逻辑记录号 → 一级索引 → 二级索引 → 实际数据块
计算示例:
-
假设:
-
块大小=4KB
-
索引项=4B
-
则每块可存1024个索引项
-
-
最大文件长度:
-
直接索引:12×4KB=48KB
-
一级间接:1024×4KB=4MB
-
二级间接:1024²×4KB=4GB
-
6.3 文件物理结构
6.3.1 连续分配
实现方式:
-
文件控制块记录:
struct contiguous_file { int start_block; // 起始块号 int block_count; // 占用块数 };
特点:
-
优点:
-
顺序访问性能最优(最少寻道时间)
-
实现简单
-
-
缺点:
-
外部碎片严重
-
文件扩展困难
-
6.3.2 链接分配
(1) 隐式链接
-
文件分配表(FAT)结构:
块号 下一块指针 0 -1 (结束) 1 4 2 3 -
访问第n块需要n次读操作
(2) 显式链接
-
FAT表内存缓存:
class FAT: def __init__(self, disk_size, block_size): self.table = [-1] * (disk_size // block_size) def allocate_chain(self, size): # 分配空闲块链 pass
6.3.3 索引分配
混合索引示例:
struct inode { int direct[12]; // 直接指针 int single_indir; // 一级间接 int double_indir; // 二级间接 int triple_indir; // 三级间接 };
最大文件计算(块大小=4KB,指针=4B):
-
直接块:12×4KB = 48KB
-
一级间接:(4KB/4B)×4KB = 4MB
-
二级间接:1024×4MB = 4GB
-
三级间接:1024×4GB = 4TB
6.4 目录管理
6.4.1 目录结构
(1) 单级目录
/ ├── file1 ├── file2 └── file3
-
问题:命名冲突,无法组织复杂项目
(2) 树形目录
/ ├── home/ │ ├── user1/ │ └── user2/ └── etc/ ├── passwd └── network/
-
支持绝对路径和相对路径
-
每个目录是特殊的inode
6.4.2 目录操作
关键系统调用:
// 创建目录 int mkdir(const char *path, mode_t mode); // 删除目录 int rmdir(const char *path); // 打开目录 DIR *opendir(const char *name); // 读取目录项 struct dirent *readdir(DIR *dirp);
6.5 文件存储空间管理
6.5.1 空闲空间管理
(1) 空闲表法
class FreeSpaceTable:
def __init__(self):
self.blocks = [(0, 1024)] # (起始块号, 连续块数)
def allocate(self, size):
# 首次适应算法
for i, (start, count) in enumerate(self.blocks):
if count >= size:
del self.blocks[i]
if count > size:
self.blocks.append((start+size, count-size))
return start
return -1 # 分配失败
(2) 位示图法
计算公式:
-
块号→位图位置:
def block_to_bit(block_num): word = block_num // 32 # 假设字长32位 bit = block_num % 32 return (word, bit)
-
磁盘空间需求:
def bitmap_size(disk_size, block_size): bits_needed = disk_size / block_size return math.ceil(bits_needed / 8) # 字节数
6.5.2 成组链接法(UNIX方案)
超级块结构:
struct super_block { int free_block_count; // 当前组空闲块数 int free_stack[100]; // 空闲块栈 int next_group; // 下一组指针 };
6.6 文件共享与保护
6.6.1 共享方式
(1) 硬链接
(2) 符号链接
-
特点:
-
是独立的文件(有自己的inode)
-
存储目标文件路径
-
跨文件系统可用
-
6.6.2 访问控制
(1) 访问控制表(ACL)权限模型
bash
# Linux setfacl示例 $ setfacl -m u:alice:rw- file.txt $ getfacl file.txt # file: file.txt # owner: bob # group: staff user::rw- user:alice:rw- group::r-- mask::rw- other::r--
(2) 文件保护矩阵
用户/组 | 文件A | 文件B | 目录C |
---|---|---|---|
user1 | rw- | r-- | --x |
groupA | r-- | rwx | r-x |
others | --- | r-- | --- |
6.7 文件系统性能
6.7.1 磁盘缓存
缓存策略:
-
预读(Read-ahead):
// 内核预读算法示例 if (sequential_access_pattern) { readahead(fd, next_blocks); }
-
延迟写(Delayed Write):
-
修改先保存在缓存中
-
定期或缓存满时同步到磁盘
-
6.7.2 磁盘调度
初始条件:
-
请求序列:
[98, 183, 37, 122, 14, 124, 65, 67]
-
磁头初始位置:
53
-
初始方向:向外(磁道号增大方向)
1. SCAN算法(电梯算法)
规则:磁头单向移动服务请求,直到磁盘最外圈(或最内圈)后反向。
调度过程:
-
当前方向:向外(磁道号↑)
-
从53出发,向外移动,依次处理路径上的请求:
-
53 → 65 → 67 → 98 → 122 → 124 → 183
(到达最外圈后反向)
-
-
反向移动(向内),处理剩余请求:
-
183 → 37 → 14
(到达最内圈结束)
-
服务顺序:
65 → 67 → 98 → 122 → 124 → 183 → 37 → 14
总寻道距离计算:
|65-53| + |67-65| + |98-67| + |122-98| + |124-122| + |183-124| + |37-183| + |14-37| = 12 + 2 + 31 + 24 + 2 + 59 + 146 + 23 = 299
2. SSTF算法(最短寻道时间优先)
规则:每次选择离当前磁头位置最近的请求。
调度过程:
-
初始位置:53
-
最近请求:
65
(距离12)
-
-
位置:65
-
最近请求:
67
(距离2)
-
-
位置:67
-
最近请求:
37
(距离30)或98
(距离31)→ 选择37
-
-
位置:37
-
最近请求:
14
(距离23)
-
-
位置:14
-
最近请求:
98
(距离84)
-
-
位置:98
-
最近请求:
122
(距离24)
-
-
位置:122
-
最近请求:
124
(距离2)
-
-
位置:124
-
最近请求:
183
(距离59)
-
服务顺序:
65 → 67 → 37 → 14 → 98 → 122 → 124 → 183
总寻道距离计算:
|65-53| + |67-65| + |37-67| + |14-37| + |98-14| + |122-98| + |124-122| + |183-124| = 12 + 2 + 30 + 23 + 84 + 24 + 2 + 59 = 236
3. C-SCAN算法(循环扫描)
规则:磁头单向移动服务请求,到达最外圈后直接跳回最内圈(不处理返回时的请求)。
调度过程:
-
当前方向:向外(磁道号↑)
-
从53出发,向外移动,依次处理路径上的请求:
-
53 → 65 → 67 → 98 → 122 → 124 → 183
(到达最外圈)
-
-
直接跳回最内圈(
0
,假设最小磁道为0),继续向外处理剩余请求:-
0 → 14 → 37
-
服务顺序:
65 → 67 → 98 → 122 → 124 → 183 → 14 → 37
总寻道距离计算:
|65-53| + |67-65| + |98-67| + |122-98| + |124-122| + |183-124| + |0-183| + |14-0| + |37-14| = 12 + 2 + 31 + 24 + 2 + 59 + 183 + 14 + 23 = 350
对比总结(基于本题)
算法 | 服务顺序 | 总寻道距离 | 特点 |
---|---|---|---|
SCAN | 65→67→98→122→124→183→37→14 | 299 | 到达端点才反向 |
SSTF | 65→67→37→14→98→122→124→183 | 236 | 最短寻道,但可能饥饿 |
C-SCAN | 65→67→98→122→124→183→14→37 | 350 | 单向移动,跳回最内圈 |
结论:
-
SSTF 寻道距离最短(
236
),但可能导致饥饿。 -
SCAN(
299
),适合高负载系统。 -
C-SCAN 提供更均匀的等待时间,但寻道距离较长。
第七章 设备管理
7.1 I/O系统概述
7.1.1 设备分类
按传输速率分类:
类型 | 速率范围 | 典型设备 | 管理特点 |
---|---|---|---|
低速设备 | <1KB/s | 键盘、鼠标 | 以字符为单位传输 |
中速设备 | 1KB/s-1MB/s | 打印机、扫描仪 | 需缓冲管理 |
高速设备 | >1MB/s | 磁盘、网卡 | DMA或通道控制 |
按信息组织方式分类:
7.1.2 设备控制器功能
-
接口转换:将设备信号转换为标准电子接口
-
缓冲暂存:缓解I/O速度与CPU差异
-
错误检测:校验数据传输的正确性
-
地址识别:响应特定I/O端口或内存地址
7.2 I/O控制方式
7.2.1 四种控制方式对比
控制方式 | CPU介入程度 | 数据传输单位 | 典型应用场景 | 硬件需求 |
---|---|---|---|---|
程序轮询 | 100% | 字节/字 | 简单嵌入式系统 | 状态寄存器 |
中断驱动 | 每次传输 | 字节/字 | 键盘、鼠标 | 中断控制器 |
DMA | 开始和结束 | 数据块 | 磁盘、网卡 | DMA控制器 |
通道控制 | 几乎不介入 | 一组数据块 | 高性能存储系统 | 专用I/O处理器 |
7.2.2 DMA工作流程
关键寄存器:
-
MAR(内存地址寄存器)
-
DC(数据计数器)
-
CR(控制寄存器)
7.3 缓冲技术
7.3.1 缓冲类型
单缓冲
-
特点:生产者与消费者必须交替使用
-
问题:效率低(等待缓冲空闲)
双缓冲(乒乓缓冲)
-
优势:实现生产者消费者并行
-
应用:视频播放、数据采集
循环缓冲
7.3.2 缓冲池管理
数据结构设计:
struct buffer_header {
int device;
int status;
struct buffer_header *next;
char *data;
};
struct buffer_pool {
struct buffer_header *free_list;
struct buffer_header *io_queue;
};
操作原语:
-
get_buf()
:从空闲链获取缓冲区 -
put_buf()
:将缓冲区放回空闲链 -
request()
:设备I/O请求排队 -
release()
:完成I/O后释放
7.4 设备分配与回收
7.4.1 设备分配策略
静态分配
-
特点:进程运行前分配所有所需设备
-
优点:无死锁风险
-
缺点:资源利用率低
动态分配
-
安全分配:
-
银行家算法扩展:将设备作为一类资源纳入检测
7.4.2 设备分配过程
对于配备通道的计算机中,在进行设备分配时所需的数据结构有:设备控制表、控制器控制表、通道控制表和系统设备表等。
基本的设备分配程序
1) 分配设备: 根据物理设备名在SDT中找出该设备的DCT,若设备忙,便将请求I/O的进程PCB挂在设备队列上;否则,便按照一定的算法来计算本次设备分配的安全性,若不会导致系统进入不安全状态,便将设备分配给请求进程;否则,仍将其PCB插入设备队列。
2) 分配控制器: 分配设备给进程后,再到其DCT中找出与该设备连接的控制器的COCT。若控制器忙,便将请求I/O进程的PCB挂在该控制器的等待队列上;否则,将该控制器分配给进程。
3) 分配通道: 分配控制器后,再在COCT中找到与该控制器连接的CHCT。若通道忙,便将请求I/O的进程挂在该通道的等待队列上;否则,将该通道分配给进程。
只有在设备、控制器和通道三者都分配成功时,这次的设备分配才算成功;之后便可启动该I/O设备进行数据传送。
7.5 磁盘管理
7.5.1 磁盘调度算法(第六章讲过)
7.5.2 磁盘性能计算
访问时间组成:
总访问时间 = 寻道时间 + 旋转延迟 + 传输时间
-
典型值:
-
寻道时间:2-10ms
-
旋转延迟(7200RPM):4.17ms(半圈)
-
传输速率:200MB/s
-
示例计算:
读取4KB数据块:
总时间 = 6ms(寻道) + 4.17ms(旋转) + (4KB/200MB/s) ≈ 10.19ms
7.6 设备驱动程序
7.6.1 驱动结构
struct device_driver {
int (*init)(void);
int (*open)(struct file *);
int (*read)(struct file *, char *, int);
int (*write)(struct file *, const char *, int);
int (*ioctl)(struct file *, unsigned int, unsigned long);
int (*release)(struct file *);
};
7.6.2 驱动加载流程
-
注册设备:
-
初始化操作集:
-
创建设备节点:
7.7 关键问题解决方案
7.7.1 中断处理优化
两阶段处理:
-
上半部(快速处理):
-
在关中断环境下执行
-
只做关键状态保存
-
调度下半部处理
-
-
下半部(延迟处理):
-
可中断环境下执行
-
复杂的数据处理
-
通过tasklet/workqueue实现
-
7.7.2 设备无关性实现
统一接口层设计: