文章概述
本篇主要讲解第六章和第七章和第八章:操作系统作业管理、死锁、操作系统安全。其余章节请参考专栏其他篇幅。
目录
六、作业管理
作业管理是操作系统的重要功能,负责管理用户提交的作业,包括作业的提交、调度、执行和完成。作业管理连接用户和系统,是用户使用计算机的桥梁。
6.1 作业管理概述
作业管理是操作系统对用户作业进行管理的功能模块。在批处理系统中,作业管理尤为重要;在现代交互式系统中,作业管理的概念仍然存在,但形式有所变化。
6.1.1 作业的概念
作业的定义
作业(Job)是用户提交给计算机系统的一个相对独立的工作单位,通常包含程序、数据和作业说明书。
作业:
定义:
- 用户提交给系统的一个完整任务
- 包含完成该任务所需的所有信息
- 系统处理的基本单位
特点:
1. 独立性:
- 作业是相对独立的工作单位
- 可以独立完成
- 不依赖其他作业
2. 完整性:
- 包含完成任务的完整信息
- 程序、数据、控制信息
- 可以独立执行
3. 批处理性:
- 在批处理系统中重要
- 可以批量提交
- 系统自动处理
形象比喻:
如果把计算机系统比作一个工厂:
- 作业 = 一个完整的生产订单(包含产品规格、原材料、生产步骤)
- 程序 = 生产步骤说明(如何生产)
- 数据 = 原材料(需要处理的数据)
- 作业管理 = 订单管理系统(接收订单、安排生产、跟踪进度)
作业 vs 进程
作业和进程是不同层次的概念。
作业 vs 进程:
作业:
- 用户角度的概念
- 用户提交的任务
- 可能包含多个程序
- 从提交到完成
进程:
- 系统角度的概念
- 程序的执行实例
- 系统调度的单位
- 从创建到终止
关系:
- 一个作业可以包含多个进程
- 作业是进程的集合
- 作业完成后,相关进程终止
例子:
作业:编译C程序
包含:
- 编译程序(进程1)
- 链接程序(进程2)
- 运行程序(进程3)
一个作业 → 多个进程
6.1.2 作业的组成
一个完整的作业通常由以下几部分组成。
作业的组成:
1. 程序:
- 可执行的程序代码
- 完成作业的主要逻辑
- 例如:编译程序、计算程序
2. 数据:
- 程序处理的数据
- 输入数据文件
- 例如:源代码、数据文件
3. 作业说明书(Job Control Language,JCL):
- 作业的控制信息
- 描述作业的执行要求
- 例如:资源需求、执行步骤
4. 作业控制块(JCB,Job Control Block):
- 系统为作业创建的数据结构
- 记录作业的状态和信息
- 用于作业管理
作业说明书(JCL)
作业说明书描述作业的执行要求和控制信息。
作业说明书(JCL):
内容:
1. 作业标识:
- 作业名
- 作业号
- 用户标识
2. 资源需求:
- CPU时间
- 内存大小
- 外设需求
- 文件需求
3. 执行步骤:
- 作业的执行步骤
- 程序执行顺序
- 步骤间的依赖关系
4. 输入输出:
- 输入文件
- 输出文件
- 错误处理
例子(简化JCL):
//JOB1 JOB USER=USER1
//STEP1 EXEC PGM=COMPILE
//INPUT DD DSN=SOURCE.C
//OUTPUT DD DSN=OBJECT.O
//STEP2 EXEC PGM=LINK
//INPUT DD DSN=OBJECT.O
//OUTPUT DD DSN=PROGRAM.EXE
//STEP3 EXEC PGM=PROGRAM.EXE
//INPUT DD DSN=DATA.DAT
//OUTPUT DD DSN=RESULT.OUT
说明:
- JOB1:作业名
- STEP1, STEP2, STEP3:执行步骤
- PGM:程序名
- DD:数据定义
- DSN:数据集名
作业控制块(JCB)
作业控制块是系统为作业创建的数据结构,记录作业的所有信息。
作业控制块(JCB):
结构:
struct job_control_block {
// 作业标识
char job_name[64]; // 作业名
int job_id; // 作业ID
int user_id; // 用户ID
// 作业状态
int job_status; // 作业状态
time_t submit_time; // 提交时间
time_t start_time; // 开始时间
time_t finish_time; // 完成时间
// 资源需求
int cpu_time; // CPU时间需求
int memory_size; // 内存需求
int priority; // 优先级
// 程序和数据
char program_name[256]; // 程序名
char data_files[][256]; // 数据文件列表
// 执行步骤
struct step steps[]; // 执行步骤
// 其他信息
int exit_code; // 退出码
char error_message[512]; // 错误信息
};
作用:
- 系统管理作业的数据结构
- 记录作业的所有信息
- 用于作业调度和管理
6.1.3 作业的状态
作业在执行过程中会经历多个状态,状态转换反映了作业的生命周期。
作业状态:
1. 提交态(Submitted):
- 用户提交作业
- 作业进入系统
- 等待被调度
2. 后备态(Backup):
- 作业在磁盘上等待
- 已进入作业队列
- 等待作业调度
3. 运行态(Running):
- 作业被调度
- 创建进程执行
- 正在执行中
4. 完成态(Completed):
- 作业执行完成
- 输出结果
- 等待清理
5. 终止态(Terminated):
- 作业被终止
- 可能正常完成或异常终止
- 资源已回收
作业状态转换
作业状态转换:
状态转换图:
提交态
↓(进入系统)
后备态
↓(作业调度)
运行态
↓(执行完成/异常)
完成态
↓(清理)
终止态
详细转换:
1. 提交态 → 后备态:
- 用户提交作业
- 系统接收作业
- 创建JCB
- 加入后备队列
2. 后备态 → 运行态:
- 作业调度程序选择作业
- 分配资源
- 创建进程
- 开始执行
3. 运行态 → 完成态:
- 作业执行完成
- 正常结束或异常终止
- 保存结果
4. 完成态 → 终止态:
- 输出结果给用户
- 回收资源
- 删除JCB
- 作业完全结束
作业状态转换示例
作业状态转换示例:
时间线:
T1: 用户提交作业
状态:提交态
操作:创建JCB,加入系统
T2: 系统接收作业
状态:后备态
操作:加入后备队列,等待调度
T3: 作业调度程序选择该作业
状态:运行态
操作:分配资源,创建进程,开始执行
T4: 作业执行中
状态:运行态
操作:进程执行,使用资源
T5: 作业执行完成
状态:完成态
操作:保存结果,准备输出
T6: 输出结果,清理资源
状态:终止态
操作:删除JCB,作业结束
作业状态的管理
作业状态管理:
状态记录:
- JCB中记录当前状态
- 状态转换时更新JCB
- 系统可以查询作业状态
状态队列:
- 后备队列:所有后备态作业
- 运行队列:所有运行态作业
- 完成队列:所有完成态作业
状态查询:
- 用户可以查询作业状态
- 系统提供状态查询命令
- 例如:qstat, jobs等
状态显示:
作业ID 作业名 状态 提交时间 开始时间
──────────────────────────────────────────────
1001 job1 运行中 10:00 10:05
1002 job2 后备 10:10 -
1003 job3 完成 09:50 09:55
6.2 作业调度
作业调度(Job Scheduling)是作业管理的核心,负责从后备队列中选择作业,为其分配资源,创建进程,使其进入运行态。作业调度是高级调度,决定哪些作业可以进入系统执行。
6.2.1 作业调度的功能
作业调度程序(Job Scheduler)需要完成以下主要功能。
作业调度功能:
1. 作业选择:
- 从后备队列中选择作业
- 根据调度算法选择
- 考虑系统资源
2. 资源分配:
- 为作业分配内存
- 分配I/O设备
- 分配其他资源
3. 进程创建:
- 为作业创建进程
- 初始化进程环境
- 启动进程执行
4. 作业控制:
- 监控作业执行
- 处理作业完成
- 回收资源
5. 作业管理:
- 维护作业队列
- 更新作业状态
- 记录作业信息
作业调度的时机
作业调度时机:
1. 新作业提交:
- 用户提交新作业
- 可能触发调度
- 检查是否可以立即执行
2. 作业完成:
- 作业执行完成
- 释放资源
- 可以调度新作业
3. 系统资源变化:
- 资源释放
- 可以调度等待的作业
- 提高资源利用率
4. 定时调度:
- 定期检查
- 调度等待的作业
- 平衡系统负载
作业调度的目标
作业调度目标:
1. 提高系统吞吐量:
- 处理更多作业
- 提高系统效率
- 充分利用资源
2. 提高资源利用率:
- 充分利用CPU
- 充分利用内存
- 充分利用I/O设备
3. 公平性:
- 公平对待所有作业
- 避免作业饥饿
- 平衡用户需求
4. 响应时间:
- 减少作业等待时间
- 提高响应速度
- 改善用户体验
5. 系统稳定性:
- 避免系统过载
- 保证系统稳定
- 防止资源耗尽
6.2.2 作业调度算法
作业调度算法决定如何从后备队列中选择作业。不同的算法有不同的特点和适用场景。
1. 先来先服务(FCFS,First Come First Served)
先来先服务(FCFS):
算法:
- 按照作业提交的顺序
- 先提交的先执行
- 简单公平
特点:
- 非抢占式
- 简单易实现
- 公平但效率可能不高
例子:
后备队列:
作业1(提交时间10:00,执行时间2小时)
作业2(提交时间10:05,执行时间10分钟)
作业3(提交时间10:10,执行时间30分钟)
执行顺序:作业1 → 作业2 → 作业3
问题:
- 作业2和作业3需要等待作业1完成
- 短作业等待长作业
- 平均等待时间长
优点:
- 实现简单
- 公平
- 适合批处理系统
缺点:
- 短作业可能等待长作业
- 平均等待时间长
- 不适合交互式系统
2. 短作业优先(SJF,Shortest Job First)
短作业优先(SJF):
算法:
- 选择执行时间最短的作业
- 优先执行短作业
- 减少平均等待时间
特点:
- 非抢占式或抢占式
- 理论最优(非抢占式)
- 但需要知道执行时间
例子:
后备队列:
作业1(执行时间2小时)
作业2(执行时间10分钟)
作业3(执行时间30分钟)
执行顺序:作业2 → 作业3 → 作业1
优势:
- 短作业先执行
- 平均等待时间短
- 系统吞吐量高
优点:
- 平均等待时间最短(理论)
- 系统吞吐量高
- 适合批处理系统
缺点:
- 长作业可能饥饿
- 需要知道执行时间(难以预测)
- 不适合交互式系统
3. 优先级调度(Priority Scheduling)
优先级调度:
算法:
- 每个作业有优先级
- 选择优先级最高的作业
- 体现作业重要性
优先级确定:
1. 静态优先级:
- 作业提交时确定
- 运行期间不变
- 简单但不灵活
2. 动态优先级:
- 运行期间可以变化
- 根据等待时间等因素调整
- 灵活但复杂
优先级因素:
- 用户优先级
- 作业类型
- 资源需求
- 等待时间
例子:
后备队列:
作业1(优先级3,执行时间2小时)
作业2(优先级1,执行时间10分钟)
作业3(优先级4,执行时间30分钟)
执行顺序:作业3 → 作业1 → 作业2
(优先级高的先执行)
优点:
- 灵活
- 可以体现重要性
- 适合多种场景
缺点:
- 低优先级可能饥饿
- 需要防止优先级反转
- 需要合理设置优先级
4. 高响应比优先(HRRN,Highest Response Ratio Next)
高响应比优先(HRRN):
算法:
- 计算每个作业的响应比
- 选择响应比最高的作业
- 平衡等待时间和执行时间
响应比计算:
响应比 = (等待时间 + 执行时间) / 执行时间
= 1 + 等待时间 / 执行时间
特点:
- 等待时间越长,响应比越高
- 执行时间越短,响应比越高
- 平衡公平性和效率
例子:
当前时间:10:30
后备队列:
作业1(提交时间10:00,执行时间2小时)
等待时间:30分钟
响应比 = 1 + 30/120 = 1.25
作业2(提交时间10:20,执行时间10分钟)
等待时间:10分钟
响应比 = 1 + 10/10 = 2.0
作业3(提交时间10:25,执行时间30分钟)
等待时间:5分钟
响应比 = 1 + 5/30 = 1.17
执行顺序:作业2 → 作业1 → 作业3
(响应比高的先执行)
优点:
- 平衡等待时间和执行时间
- 避免长作业饥饿
- 短作业也有优势
缺点:
- 需要计算响应比
- 需要知道执行时间
- 实现稍复杂
5. 多级队列调度(Multilevel Queue)
多级队列调度:
算法:
- 将作业分为多个队列
- 每个队列有不同优先级
- 高优先级队列优先调度
- 队列内可以使用不同算法
队列分类:
1. 系统作业队列(最高优先级):
- 系统作业
- 使用FCFS或优先级
2. 交互作业队列:
- 交互式作业
- 使用时间片轮转
3. 批处理作业队列(最低优先级):
- 批处理作业
- 使用FCFS或SJF
调度策略:
- 高优先级队列优先
- 队列空时才调度下一队列
- 队列内使用相应算法
例子:
队列1(系统作业,FCFS):
作业A, 作业B
队列2(交互作业,RR,时间片=10ms):
作业C, 作业D, 作业E
队列3(批处理,SJF):
作业F, 作业G
调度:队列1 → 队列2 → 队列3
优点:
- 灵活
- 适应不同作业类型
- 平衡各种需求
缺点:
- 实现复杂
- 需要合理分类
- 低优先级可能饥饿
作业调度算法对比
作业调度算法对比:
算法 平均等待时间 公平性 实现复杂度 适用场景
─────────────────────────────────────────────
FCFS 长 好 简单 批处理
SJF 最短 差 中等 批处理
优先级 中 中 中等 通用
HRRN 较短 较好 中等 批处理
多级队列 中 较好 复杂 通用系统
现代系统:
- 交互式系统:主要使用进程调度
- 批处理系统:使用作业调度
- 现代系统:作业调度概念弱化
6.2.3 作业调度与进程调度的关系
作业调度和进程调度是不同层次的调度,它们相互配合,共同完成系统调度任务。
作业调度 vs 进程调度:
层次关系:
作业调度(高级调度)
↓
进程调度(低级调度)
作业调度决定哪些作业进入系统
进程调度决定哪个进程获得CPU
功能对比:
作业调度:
- 从外存选择作业进入内存
- 创建进程
- 频率低(分钟级)
进程调度:
- 从内存选择进程分配CPU
- 进程切换
- 频率高(毫秒级)
关系:
- 作业调度为进程调度提供进程
- 进程调度执行作业的任务
- 两者配合完成作业执行
三级调度系统
现代操作系统通常采用三级调度系统。
三级调度系统:
1. 高级调度(作业调度):
- 从外存选择作业进入内存
- 创建进程
- 频率:分钟级或更长
2. 中级调度(内存调度):
- 将进程在内存和外存间换入换出
- 挂起和激活进程
- 频率:秒级
3. 低级调度(进程调度):
- 从就绪队列选择进程分配CPU
- 进程切换
- 频率:毫秒级
调度层次:
外存(作业) → 高级调度 → 内存(进程) →
中级调度(换入换出) → 就绪队列 →
低级调度 → CPU执行
现代系统:
- 交互式系统:高级调度弱化
- 主要使用进程调度
- 批处理系统:三级调度完整
作业调度与进程调度的协调
作业调度与进程调度的协调:
工作流程:
1. 用户提交作业
↓
2. 作业调度:选择作业,创建进程
↓
3. 进程进入就绪队列
↓
4. 进程调度:选择进程,分配CPU
↓
5. 进程执行
↓
6. 进程完成,作业完成
↓
7. 作业调度:回收资源,清理作业
协调机制:
- 作业调度控制进入系统的作业数
- 进程调度控制CPU的使用
- 两者配合保证系统稳定
资源管理:
- 作业调度:管理外存和系统级资源
- 进程调度:管理CPU资源
- 共同管理内存资源
6.3 用户接口
用户接口是用户与操作系统交互的界面,是用户使用计算机的桥梁。操作系统提供多种类型的用户接口,满足不同用户的需求。
6.3.1 命令接口
命令接口(Command Interface)是用户通过输入命令与操作系统交互的接口,也称为命令行接口(CLI,Command Line Interface)。
命令接口:
定义:
- 用户通过输入命令与系统交互
- 系统执行命令并返回结果
- 文本形式的交互
特点:
- 文本输入输出
- 命令驱动
- 精确控制
- 适合熟练用户
类型:
1. 联机命令接口(交互式)
2. 脱机命令接口(批处理)
1. 联机命令接口(交互式命令接口)
联机命令接口是用户通过终端输入命令,系统立即执行并返回结果的接口。
联机命令接口:
特点:
- 用户输入命令
- 系统立即执行
- 立即返回结果
- 交互式操作
工作过程:
1. 系统显示提示符
2. 用户输入命令
3. 系统解析命令
4. 执行命令
5. 显示结果
6. 返回提示符
例子(Linux):
$ ls -l
total 24
-rw-r--r-- 1 user user 1024 Jan 1 10:00 file1.txt
-rw-r--r-- 1 user user 2048 Jan 1 10:01 file2.txt
drwxr-xr-x 2 user user 4096 Jan 1 10:02 dir1
$
过程:
1. 系统显示提示符 $
2. 用户输入 ls -l
3. 系统执行列出文件命令
4. 显示文件列表
5. 返回提示符
命令的类型
命令类型:
1. 系统访问命令:
- login: 登录系统
- logout: 退出系统
- passwd: 修改密码
2. 文件操作命令:
- ls: 列出文件
- cat: 显示文件内容
- cp: 复制文件
- mv: 移动文件
- rm: 删除文件
- mkdir: 创建目录
- rmdir: 删除目录
3. 目录操作命令:
- cd: 改变目录
- pwd: 显示当前目录
- find: 查找文件
4. 进程管理命令:
- ps: 显示进程
- kill: 终止进程
- jobs: 显示作业
- bg/fg: 后台/前台作业
5. 系统信息命令:
- date: 显示日期时间
- who: 显示登录用户
- df: 显示磁盘使用
- top: 显示系统状态
6. 输入输出重定向:
- >: 输出重定向
- >>: 追加输出
- <: 输入重定向
- |: 管道
7. 命令组合:
- ;: 顺序执行
- &&: 逻辑与
- ||: 逻辑或
命令的格式
命令格式:
基本格式:
命令名 [选项] [参数]
例子:
ls -l /home/user
命令名:ls(列出文件)
选项:-l(长格式显示)
参数:/home/user(目录路径)
选项:
- 短选项:-l, -a, -h
- 长选项:--long, --all, --human-readable
- 可以组合:-lah
参数:
- 命令操作的对象
- 文件、目录等
- 可以有多个参数
2. 脱机命令接口(批处理命令接口)
脱机命令接口是用户将多个命令写入文件,系统批量执行的接口。
脱机命令接口:
定义:
- 用户将命令写入脚本文件
- 系统批量执行
- 也称为批处理
特点:
- 命令预先写好
- 批量执行
- 不需要交互
- 适合重复任务
脚本文件:
- 包含多个命令
- 可以包含控制结构
- 可以包含变量
- 可以包含函数
例子(Shell脚本):
#!/bin/bash
# 批处理脚本示例
echo "开始处理"
ls -l
cp file1.txt file2.txt
echo "处理完成"
执行:
$ bash script.sh
或
$ ./script.sh(如果可执行)
优势:
- 可以重复执行
- 可以自动化任务
- 适合系统管理
- 提高效率
命令接口的优缺点
命令接口优缺点:
优点:
1. 精确控制:
- 可以精确控制操作
- 功能强大
- 适合高级用户
2. 效率高:
- 熟练用户操作快
- 可以批量操作
- 可以脚本化
3. 资源占用少:
- 不需要图形界面
- 资源占用少
- 适合服务器
4. 远程操作:
- 可以通过网络操作
- 适合远程管理
- 灵活方便
缺点:
1. 学习曲线陡:
- 需要记忆命令
- 需要学习语法
- 不适合新手
2. 容易出错:
- 命令输入错误
- 参数错误
- 需要仔细
3. 不够直观:
- 文本界面
- 不够直观
- 需要想象
使用场景:
- 系统管理员
- 程序员
- 服务器管理
- 自动化脚本
6.3.2 程序接口
程序接口(Program Interface)是应用程序调用操作系统功能的接口,也称为应用程序接口(API,Application Programming Interface)。
程序接口:
定义:
- 应用程序调用操作系统功能的接口
- 通过系统调用实现
- 程序与系统交互的桥梁
特点:
- 面向程序员
- 函数调用形式
- 提供系统功能
- 隐藏系统细节
作用:
1. 提供系统功能:
- 文件操作
- 进程管理
- 内存管理
- 设备管理
2. 隐藏系统细节:
- 封装系统实现
- 简化编程
- 提高可移植性
3. 保护系统:
- 通过接口访问
- 控制访问权限
- 保护系统安全
程序接口的类型
程序接口类型:
1. 系统调用(System Call):
- 直接调用操作系统功能
- 底层接口
- 需要切换到内核态
2. 库函数(Library Function):
- 封装系统调用
- 提供高级功能
- 在用户态执行
3. API(Application Programming Interface):
- 应用程序接口
- 可以是系统调用或库函数
- 提供统一接口
关系:
应用程序 → 库函数 → 系统调用 → 操作系统
系统调用示例
系统调用示例:
文件操作:
int open(const char *pathname, int flags);
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
int close(int fd);
进程管理:
pid_t fork(void);
int execve(const char *pathname, char *const argv[]);
pid_t wait(int *wstatus);
int kill(pid_t pid, int sig);
内存管理:
void *malloc(size_t size);
void free(void *ptr);
void *mmap(void *addr, size_t length, int prot, int flags);
网络通信:
int socket(int domain, int type, int protocol);
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
程序接口的使用
程序接口使用示例:
C语言示例:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main() {
// 打开文件(系统调用)
int fd = open("file.txt", O_RDONLY);
if (fd < 0) {
perror("open failed");
return 1;
}
// 读取文件(系统调用)
char buf[100];
ssize_t n = read(fd, buf, sizeof(buf));
// 写入标准输出(库函数,内部调用系统调用)
printf("Read %zd bytes: %s\n", n, buf);
// 关闭文件(系统调用)
close(fd);
return 0;
}
说明:
- open, read, close是系统调用
- printf是库函数(内部调用write系统调用)
- 程序通过接口使用系统功能
6.3.3 图形用户接口
图形用户接口(GUI,Graphical User Interface)是用户通过图形界面与操作系统交互的接口。
图形用户接口:
定义:
- 使用图形元素与用户交互
- 窗口、图标、菜单等
- 鼠标点击操作
特点:
- 直观易用
- 图形化界面
- 鼠标操作
- 适合普通用户
组成元素:
1. 窗口(Window)
2. 图标(Icon)
3. 菜单(Menu)
4. 按钮(Button)
5. 对话框(Dialog)
6. 工具栏(Toolbar)
GUI的组成
GUI组成:
1. 窗口系统:
- 管理窗口
- 窗口创建、移动、关闭
- 窗口重叠、切换
2. 图形库:
- 绘制图形元素
- 处理图形输出
- 提供图形API
3. 输入处理:
- 鼠标输入
- 键盘输入
- 触摸输入(移动设备)
4. 事件处理:
- 处理用户事件
- 鼠标点击、键盘输入
- 窗口事件
5. 应用程序框架:
- 提供应用程序框架
- 简化GUI程序开发
- 例如:Qt, GTK, Win32 API
GUI的工作过程
GUI工作过程:
1. 用户操作:
- 鼠标点击按钮
- 键盘输入文字
- 窗口操作
2. 事件产生:
- 系统捕获用户操作
- 产生相应事件
- 例如:鼠标点击事件
3. 事件分发:
- 系统将事件分发给应用程序
- 应用程序处理事件
- 执行相应操作
4. 界面更新:
- 应用程序更新界面
- 显示结果
- 反馈给用户
例子:
用户点击"保存"按钮:
1. 鼠标点击事件
2. 系统捕获事件
3. 分发给应用程序
4. 应用程序执行保存操作
5. 更新界面显示"已保存"
GUI的优缺点
GUI优缺点:
优点:
1. 直观易用:
- 图形界面直观
- 不需要记忆命令
- 适合普通用户
2. 操作简单:
- 鼠标点击操作
- 拖放操作
- 操作简单
3. 信息丰富:
- 可以显示丰富信息
- 图形、图像、动画
- 信息表达清晰
4. 错误提示友好:
- 图形化错误提示
- 帮助信息
- 用户友好
缺点:
1. 资源占用多:
- 需要图形系统
- 占用内存和CPU
- 资源消耗大
2. 操作效率可能低:
- 某些操作不如命令行快
- 需要多次点击
- 熟练用户可能觉得慢
3. 远程操作不便:
- 需要图形传输
- 网络带宽要求高
- 延迟可能较大
使用场景:
- 桌面系统
- 普通用户
- 图形应用
- 现代操作系统主要接口
现代GUI系统
现代GUI系统:
桌面系统:
1. Windows:
- Windows GUI
- Win32 API
- 现代Windows使用WPF/UWP
2. macOS:
- Aqua界面
- Cocoa框架
- 美观易用
3. Linux:
- X Window System
- GNOME, KDE等桌面环境
- 多种选择
移动系统:
1. Android:
- Material Design
- 触摸优化
- 适合移动设备
2. iOS:
- iOS界面
- 触摸优化
- 简洁美观
Web界面:
- 浏览器作为GUI
- HTML/CSS/JavaScript
- 跨平台
用户接口的选择
用户接口选择:
接口类型 适用用户 适用场景 特点
─────────────────────────────────────────────
命令接口 系统管理员 服务器管理 精确、高效
程序接口 程序员 应用程序开发 功能强大
图形接口 普通用户 桌面应用 直观、易用
现代系统:
- 通常提供多种接口
- 用户可以根据需要选择
- 不同接口适合不同场景
- 相互补充
6.4 系统调用
系统调用(System Call)是操作系统提供给应用程序的接口,是应用程序请求操作系统服务的唯一方式。系统调用是用户态程序进入内核态的桥梁,是操作系统功能的核心接口。
6.4.1 系统调用的概念
系统调用的定义
系统调用是操作系统内核提供的函数,允许用户态程序请求内核服务。
系统调用:
定义:
- 操作系统内核提供的函数
- 用户态程序调用内核功能
- 应用程序与操作系统交互的接口
特点:
1. 特权操作:
- 需要内核权限
- 用户态程序不能直接执行
- 必须通过系统调用
2. 接口统一:
- 提供统一的接口
- 隐藏内核实现细节
- 简化应用程序开发
3. 安全保护:
- 内核检查权限
- 防止非法操作
- 保护系统安全
4. 状态切换:
- 从用户态切换到内核态
- 执行内核代码
- 返回用户态
形象比喻:
如果把操作系统比作一个政府机构:
- 应用程序 = 普通市民(用户态)
- 操作系统内核 = 政府机构(内核态)
- 系统调用 = 办事窗口(系统调用接口)
- 办事流程 = 市民不能直接进入政府,必须通过窗口申请服务
市民(应用程序)通过窗口(系统调用)申请服务,政府(内核)处理请求并返回结果。
为什么需要系统调用
为什么需要系统调用:
1. 保护系统:
- 用户程序不能直接访问硬件
- 不能直接修改系统数据
- 必须通过系统调用
- 内核检查权限
2. 统一接口:
- 提供统一的接口
- 隐藏硬件差异
- 简化编程
- 提高可移植性
3. 资源管理:
- 内核统一管理资源
- 防止资源冲突
- 公平分配资源
- 提高系统效率
4. 抽象硬件:
- 隐藏硬件细节
- 提供高级抽象
- 简化应用程序
- 便于开发
系统调用的分类
系统调用分类:
1. 进程控制:
- fork, exec, exit
- wait, kill
- 进程创建、终止、控制
2. 文件操作:
- open, read, write, close
- create, delete, link
- 文件创建、读写、删除
3. 设备管理:
- ioctl, read, write
- 设备控制、读写
4. 信息维护:
- getpid, gettime
- 获取系统信息
5. 通信:
- pipe, shmget
- 进程间通信
6. 存储管理:
- brk, mmap
- 内存分配、映射
6.4.2 系统调用的类型
系统调用可以按功能分为多个类型,每类提供相关的系统功能。
1. 进程控制类系统调用
进程控制类系统调用:
1. fork():
- 创建子进程
- 返回子进程PID(父进程)或0(子进程)
- Unix/Linux使用
2. exec()系列:
- 执行新程序
- 替换当前进程映像
- execve, execl等
3. exit():
- 终止进程
- 返回退出码
- 释放资源
4. wait() / waitpid():
- 等待子进程终止
- 获取子进程退出状态
- 清理僵尸进程
5. kill():
- 向进程发送信号
- 可以终止进程
- 进程间通信
例子:
pid_t pid = fork();
if (pid == 0) {
// 子进程
execve("/bin/ls", argv, envp);
} else {
// 父进程
wait(&status);
}
2. 文件操作类系统调用
文件操作类系统调用:
1. open():
- 打开文件
- 返回文件描述符
- 指定打开方式
2. read():
- 从文件读取数据
- 返回读取的字节数
- 可能阻塞
3. write():
- 向文件写入数据
- 返回写入的字节数
- 可能阻塞
4. close():
- 关闭文件
- 释放文件描述符
- 刷新缓冲区
5. lseek():
- 改变文件指针位置
- 支持随机访问
- 返回新位置
6. stat() / fstat():
- 获取文件属性
- 文件大小、权限等
- 文件元数据
例子:
int fd = open("file.txt", O_RDONLY);
char buf[100];
ssize_t n = read(fd, buf, sizeof(buf));
close(fd);
3. 设备管理类系统调用
设备管理类系统调用:
1. ioctl():
- 设备控制
- 设备特定操作
- 灵活但复杂
2. read() / write():
- 设备读写
- 统一接口
- 设备抽象为文件
例子:
// 设置串口波特率
ioctl(fd, TIOCSBRK, &baudrate);
// 读取设备数据
read(device_fd, buf, size);
4. 信息维护类系统调用
信息维护类系统调用:
1. getpid():
- 获取进程ID
- 当前进程标识
2. getuid() / getgid():
- 获取用户ID
- 获取组ID
3. time():
- 获取系统时间
- 时间戳
4. sysinfo():
- 获取系统信息
- 系统状态
例子:
pid_t pid = getpid();
uid_t uid = getuid();
time_t now = time(NULL);
5. 通信类系统调用
通信类系统调用:
1. pipe():
- 创建管道
- 进程间通信
- 单向通信
2. shmget() / shmat():
- 共享内存
- 创建和连接
- 高效通信
3. msgget() / msgsnd() / msgrcv():
- 消息队列
- 发送和接收消息
- 进程间通信
4. socket():
- 创建套接字
- 网络通信
- 本地和网络
例子:
int pipefd[2];
pipe(pipefd);
// pipefd[0]读端,pipefd[1]写端
6. 存储管理类系统调用
存储管理类系统调用:
1. brk() / sbrk():
- 改变数据段大小
- 内存分配
- 底层内存管理
2. mmap() / munmap():
- 内存映射
- 文件映射到内存
- 共享内存
3. mlock() / munlock():
- 锁定内存
- 防止换出
- 实时应用
例子:
void *addr = mmap(NULL, size, PROT_READ|PROT_WRITE,
MAP_SHARED, fd, 0);
// 将文件映射到内存
6.4.3 系统调用的实现
系统调用的实现涉及用户态到内核态的切换,是操作系统的核心机制。
系统调用的实现机制
系统调用实现机制:
1. 系统调用号:
- 每个系统调用有唯一编号
- 系统调用表索引
- 例如:open=2, read=0, write=1
2. 系统调用表:
- 内核中的函数指针数组
- 索引是系统调用号
- 值是处理函数地址
3. 调用过程:
a. 用户程序设置系统调用号和参数
b. 执行特殊指令(如int 0x80, syscall)
c. CPU切换到内核态
d. 内核查找系统调用表
e. 调用对应的处理函数
f. 执行系统调用
g. 返回用户态
h. 返回结果给用户程序
系统调用的调用过程
系统调用调用过程:
详细步骤:
1. 用户程序准备:
- 设置系统调用号(寄存器)
- 设置参数(寄存器或栈)
- 执行系统调用指令
2. 硬件处理:
- CPU检测到系统调用指令
- 保存用户态上下文
- 切换到内核态
- 跳转到系统调用处理程序
3. 内核处理:
- 系统调用处理程序运行
- 从寄存器读取系统调用号
- 查找系统调用表
- 调用对应的处理函数
4. 执行系统调用:
- 执行实际的系统调用函数
- 检查参数合法性
- 检查权限
- 执行操作
5. 返回结果:
- 将结果放入寄存器
- 恢复用户态上下文
- 返回到用户程序
- 用户程序获得结果
时间线:
用户态 → 系统调用指令 → 内核态 →
系统调用处理 → 返回 → 用户态
系统调用表
系统调用表:
结构:
sys_call_table[] = {
[0] = sys_read, // read系统调用
[1] = sys_write, // write系统调用
[2] = sys_open, // open系统调用
[3] = sys_close, // close系统调用
...
};
查找过程:
系统调用号 = 2(open)
→ sys_call_table[2]
→ sys_open函数
→ 执行open系统调用
实现:
- 系统调用表在内核中
- 系统启动时初始化
- 不同架构可能不同
系统调用的参数传递
系统调用参数传递:
方法:
1. 寄存器传递:
- 参数放在寄存器中
- 速度快
- 但寄存器数量有限
2. 栈传递:
- 参数放在栈中
- 可以传递多个参数
- 但速度稍慢
3. 混合方式:
- 前几个参数用寄存器
- 其余参数用栈
- 平衡性能和灵活性
例子(x86-64):
open系统调用:
- 系统调用号:放在rax寄存器
- 参数1(路径):放在rdi寄存器
- 参数2(标志):放在rsi寄存器
- 参数3(权限):放在rdx寄存器
- 执行syscall指令
系统调用的开销
系统调用开销:
开销来源:
1. 状态切换:
- 用户态→内核态切换
- 保存和恢复上下文
- 有一定开销
2. 参数检查:
- 检查参数合法性
- 检查权限
- 安全检查
3. 内核执行:
- 执行内核代码
- 可能涉及复杂操作
- 可能阻塞
优化方法:
1. 减少系统调用次数:
- 批量操作
- 缓存结果
- 减少切换
2. 快速系统调用:
- 使用专门的指令(如syscall)
- 减少切换开销
- 提高性能
3. 系统调用合并:
- 合并多个系统调用
- 减少切换次数
- 提高效率
典型开销:
- 系统调用本身:1-10微秒
- 加上实际操作:可能更长
- 现代系统已优化
6.4.4 系统调用与库函数的关系
系统调用和库函数是不同层次的接口,它们有密切的关系但也有区别。
系统调用 vs 库函数:
系统调用:
- 操作系统内核提供的接口
- 直接调用内核功能
- 需要切换到内核态
- 底层接口
库函数:
- 库提供的函数
- 可能在用户态执行
- 可能封装系统调用
- 高级接口
关系:
库函数可能:
1. 直接调用系统调用
2. 封装多个系统调用
3. 完全在用户态实现
4. 组合系统调用和用户态代码
库函数与系统调用的关系
库函数与系统调用的关系:
1. 直接封装系统调用:
- 库函数直接调用系统调用
- 提供更友好的接口
- 例如:fopen() → open()
2. 封装多个系统调用:
- 一个库函数调用多个系统调用
- 提供高级功能
- 例如:fprintf() → 多个write()
3. 完全用户态实现:
- 库函数在用户态实现
- 不调用系统调用
- 例如:strlen(), strcpy()
4. 混合实现:
- 部分用户态,部分系统调用
- 优化性能
- 例如:malloc() → brk() + 用户态管理
例子分析
例子分析:
1. printf():
库函数:printf()
↓
内部调用:write()系统调用
↓
内核:sys_write()
printf()是库函数
内部调用write()系统调用
2. fopen():
库函数:fopen()
↓
内部调用:open()系统调用
↓
创建FILE结构
↓
返回FILE指针
fopen()封装open()
提供更高级的接口
3. malloc():
库函数:malloc()
↓
用户态内存管理
↓
需要时调用:brk()或mmap()
↓
内核:分配内存
malloc()主要用户态实现
需要时调用系统调用
4. strlen():
库函数:strlen()
↓
完全用户态实现
↓
不调用系统调用
纯用户态函数
不需要系统调用
系统调用与库函数的对比
系统调用 vs 库函数对比:
特性 系统调用 库函数
─────────────────────────────────────
提供者 操作系统内核 库
执行位置 内核态 用户态(通常)
调用方式 特殊指令 普通函数调用
开销 较大 较小
功能 底层功能 高级功能
可移植性 系统相关 可能可移植
数量 较少(几百个) 很多(数千个)
选择:
- 需要系统功能:使用系统调用
- 需要高级功能:使用库函数
- 通常使用库函数(更友好)
- 库函数内部调用系统调用
系统调用的封装层次
系统调用封装层次:
应用程序
↓
库函数(高级接口)
↓
系统调用(底层接口)
↓
操作系统内核
例子:
应用程序:printf("Hello")
↓
库函数:fprintf(stdout, "Hello")
↓
系统调用:write(1, "Hello", 5)
↓
内核:sys_write()
↓
设备驱动:输出到终端
层次:
- 应用程序使用库函数(简单)
- 库函数调用系统调用(封装)
- 系统调用进入内核(底层)
- 内核执行操作(实际工作)
现代系统的系统调用
现代系统系统调用:
Linux:
- 系统调用号:0-400+
- 使用syscall指令(x86-64)
- 系统调用表:sys_call_table
- 文档:man 2 syscalls
Windows:
- 系统调用:通过ntdll.dll
- API:Win32 API(库函数)
- 应用程序通常使用Win32 API
- 很少直接调用系统调用
macOS:
- 系统调用:BSD系统调用
- API:Cocoa框架
- 应用程序使用高级API
- 系统调用被封装
趋势:
- 应用程序主要使用库函数
- 系统调用被封装
- 提供更高级的接口
- 简化开发
系统调用总结
系统调用总结:
1. 系统调用是应用程序与操作系统交互的唯一方式:
- 必须通过系统调用
- 不能直接访问内核
- 保护系统安全
2. 系统调用需要状态切换:
- 用户态→内核态
- 有一定开销
- 需要优化
3. 库函数封装系统调用:
- 提供更友好的接口
- 简化编程
- 提高可移植性
4. 现代系统:
- 提供丰富的系统调用
- 库函数封装系统调用
- 应用程序主要使用库函数
- 系统调用是底层基础
七、死锁
死锁是操作系统中的一个严重问题,指多个进程因竞争资源而相互等待,导致所有进程都无法继续执行。死锁会导致系统资源浪费,进程无法完成,是操作系统设计和实现中必须考虑和解决的问题。
7.1 死锁的基本概念
死锁是多个进程在竞争资源时出现的一种僵持状态,所有进程都在等待其他进程释放资源,但没有任何进程能够继续执行,导致系统无法正常工作。
7.1.1 死锁的定义
死锁的正式定义
死锁(Deadlock)是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。
死锁的定义:
正式定义:
- 一组进程处于死锁状态
- 当且仅当这组进程中的每个进程
- 都在等待该组中另一个进程占有的资源
- 且这些资源不可抢占
关键特征:
1. 多个进程参与
2. 每个进程都在等待
3. 等待的资源被其他进程占有
4. 形成循环等待
5. 无法自行解除
形象比喻:
如果把资源比作钥匙,进程比作需要钥匙的人:
- 死锁 = 四个人,每人有一把钥匙,但需要另一把钥匙才能开门
- 进程1 有钥匙A,需要钥匙B
- 进程2 有钥匙B,需要钥匙C
- 进程3 有钥匙C,需要钥匙D
- 进程4 有钥匙D,需要钥匙A
- 结果:所有人都在等待,但没有人能继续
死锁的典型例子
死锁典型例子:
哲学家就餐问题:
5个哲学家围坐圆桌
每人面前有一盘食物
每两人之间有一根筷子
需要两根筷子才能吃饭
死锁情况:
所有哲学家同时拿起左边的筷子
所有人都在等待右边的筷子
但右边的筷子被其他人拿着
所有人都在等待,无法继续
形成死锁
资源:
- 筷子(5根)
- 进程:哲学家(5个)
等待关系:
哲学家1等待哲学家2的筷子
哲学家2等待哲学家3的筷子
哲学家3等待哲学家4的筷子
哲学家4等待哲学家5的筷子
哲学家5等待哲学家1的筷子
形成循环等待
死锁的图示表示
死锁图示:
资源分配图:
进程 资源
P1 ──→ R1(已分配)
P2 ──→ R2(已分配)
P1 ──→ R2(等待)
P2 ──→ R1(等待)
形成循环:
P1 → R2 → P2 → R1 → P1
循环等待,死锁!
资源分配图规则:
- 进程 → 资源:进程请求资源
- 资源 → 进程:资源分配给进程
- 存在循环:可能死锁
- 无循环:无死锁
7.1.2 产生死锁的原因
死锁的产生需要特定的条件,主要原因包括资源竞争和进程推进顺序不当。
产生死锁的原因:
1. 竞争不可抢占资源:
- 多个进程竞争不可抢占资源
- 资源被占用后不能强制释放
- 例如:打印机、磁带机
2. 竞争可消耗资源:
- 多个进程竞争可消耗资源
- 资源使用后消失
- 例如:消息、信号
3. 进程推进顺序不当:
- 进程请求资源的顺序不当
- 可能导致循环等待
- 例如:都先请求资源A再请求资源B
4. 系统资源不足:
- 系统资源有限
- 无法满足所有进程需求
- 导致竞争和等待
1. 竞争不可抢占资源
竞争不可抢占资源:
不可抢占资源:
- 资源一旦分配,不能强制收回
- 必须等待进程主动释放
- 例如:打印机、磁带机、文件锁
死锁场景:
进程P1:已占用打印机,需要磁带机
进程P2:已占用磁带机,需要打印机
情况:
P1等待P2释放磁带机
P2等待P1释放打印机
两者互相等待,死锁!
例子:
进程1:
1. 申请打印机(获得)
2. 申请磁带机(等待)
进程2:
1. 申请磁带机(获得)
2. 申请打印机(等待)
结果:两个进程都在等待,死锁
2. 竞争可消耗资源
竞争可消耗资源:
可消耗资源:
- 资源使用后消失
- 可以产生新的资源
- 例如:消息、信号、中断
死锁场景:
进程P1:等待进程P2发送消息
进程P2:等待进程P3发送消息
进程P3:等待进程P1发送消息
情况:
所有进程都在等待消息
但没有人发送消息
形成死锁
例子:
进程1:receive(P2) // 等待P2的消息
进程2:receive(P3) // 等待P3的消息
进程3:receive(P1) // 等待P1的消息
结果:所有进程都在等待,死锁
3. 进程推进顺序不当
进程推进顺序不当:
问题:
- 进程请求资源的顺序不当
- 可能导致循环等待
- 例如:都按相同顺序请求资源
死锁场景:
所有进程都按相同顺序请求资源:
先请求资源A
再请求资源B
如果进程1占用A,请求B
进程2占用B,请求A
就会死锁
例子:
进程1:
1. 请求资源A(获得)
2. 请求资源B(等待)
进程2:
1. 请求资源B(获得)
2. 请求资源A(等待)
结果:死锁
如果顺序不同:
进程1:先A后B
进程2:先B后A
可能死锁
如果顺序相同:
进程1:先A后B
进程2:先A后B
不会死锁(进程2等待进程1释放A)
4. 系统资源不足
系统资源不足:
问题:
- 系统资源有限
- 无法满足所有进程需求
- 导致竞争和等待
死锁场景:
系统有2个资源
3个进程都需要2个资源
情况:
进程1占用2个资源
进程2和进程3各等待2个资源
但只有进程1释放资源后才有资源
如果进程1也在等待其他资源,可能死锁
例子:
系统:2个内存块
进程1:需要2个,已获得2个
进程2:需要2个,等待中
进程3:需要2个,等待中
如果进程1也在等待其他资源(如I/O)
且进程2、进程3也在等待
可能形成死锁
7.1.3 产生死锁的必要条件
死锁的产生需要同时满足四个必要条件,缺一不可。这四个条件是死锁的理论基础。
产生死锁的必要条件:
1. 互斥条件(Mutual Exclusion)
2. 请求和保持条件(Hold and Wait)
3. 不可抢占条件(No Preemption)
4. 循环等待条件(Circular Wait)
关系:
- 四个条件同时满足 → 可能死锁
- 只要破坏一个条件 → 不会死锁
- 这是死锁预防的理论基础
1. 互斥条件(Mutual Exclusion)
互斥条件:
定义:
- 资源不能被多个进程同时使用
- 同一时刻只能被一个进程使用
- 资源具有排他性
说明:
- 如果资源可以共享,不会死锁
- 但某些资源必须互斥使用
- 例如:打印机、写文件
例子:
打印机:
- 不能同时被多个进程使用
- 必须互斥访问
- 满足互斥条件
如果打印机可以共享:
- 多个进程可以同时打印
- 不会因为打印机死锁
- 但实际不可能(打印会混乱)
破坏方法:
- 使用SPOOLing技术
- 将独占设备虚拟为共享设备
- 避免互斥条件
2. 请求和保持条件(Hold and Wait)
请求和保持条件:
定义:
- 进程已经占有一些资源
- 同时请求新的资源
- 在等待新资源时不释放已有资源
说明:
- 进程保持已有资源
- 同时请求新资源
- 如果新资源不可用,继续等待
例子:
进程P1:
- 已占用资源A
- 请求资源B(等待)
- 不释放资源A
进程P2:
- 已占用资源B
- 请求资源A(等待)
- 不释放资源B
结果:互相等待,死锁
破坏方法:
- 进程必须一次性申请所有资源
- 或者申请新资源前释放已有资源
- 避免保持和等待同时发生
3. 不可抢占条件(No Preemption)
不可抢占条件:
定义:
- 进程已获得的资源不能被强制抢占
- 只能由进程主动释放
- 其他进程不能强制收回
说明:
- 资源一旦分配,不能强制收回
- 必须等待进程主动释放
- 如果资源可抢占,不会死锁
例子:
进程P1占用打印机
进程P2需要打印机
如果不可抢占:
- P2必须等待P1释放
- 如果P1也在等待其他资源
- 可能死锁
如果可以抢占:
- 系统可以强制收回打印机
- 分配给P2
- 不会死锁
破坏方法:
- 允许系统强制收回资源
- 资源可抢占
- 但可能影响进程执行
4. 循环等待条件(Circular Wait)
循环等待条件:
定义:
- 存在一个进程等待链
- 链中每个进程等待下一个进程占有的资源
- 最后一个进程等待第一个进程
- 形成循环
说明:
- 进程等待关系形成环
- P1等待P2,P2等待P3,...,Pn等待P1
- 形成循环等待
例子:
进程P1:占用R1,等待R2
进程P2:占用R2,等待R3
进程P3:占用R3,等待R1
等待链:
P1 → R2 → P2 → R3 → P3 → R1 → P1
形成循环,死锁!
破坏方法:
- 对资源编号
- 进程按编号顺序申请资源
- 避免循环等待
四个条件的关系
四个条件的关系:
必要条件:
- 四个条件都是必要条件
- 必须同时满足才可能死锁
- 破坏任何一个就不会死锁
充分性:
- 四个条件同时满足 → 可能死锁
- 但不一定必然死锁
- 还取决于进程执行顺序
死锁预防:
- 破坏互斥条件:使用SPOOLing
- 破坏请求和保持:一次性分配
- 破坏不可抢占:允许抢占
- 破坏循环等待:资源有序分配
实际应用:
- 通常破坏请求和保持或循环等待
- 最容易实现
- 效果最好
死锁的必要条件总结
死锁必要条件总结:
条件 含义 破坏方法
─────────────────────────────────────────
互斥条件 资源排他使用 SPOOLing
请求和保持 保持资源同时请求 一次性分配
不可抢占 资源不能强制收回 允许抢占
循环等待 等待关系形成环 资源有序分配
预防策略:
- 破坏一个或多个条件
- 防止死锁发生
- 但可能影响系统性能
- 需要权衡
7.2 死锁的处理方法
死锁的处理方法主要有四种:预防、避免、检测和解除。不同的方法有不同的特点和适用场景,系统可以根据需要选择合适的方法。
死锁处理方法:
1. 死锁预防(Deadlock Prevention):
- 破坏死锁的必要条件
- 防止死锁发生
- 静态方法
2. 死锁避免(Deadlock Avoidance):
- 动态检查资源分配
- 避免进入不安全状态
- 动态方法
3. 死锁检测(Deadlock Detection):
- 允许死锁发生
- 定期检测死锁
- 发现后处理
4. 死锁解除(Deadlock Recovery):
- 检测到死锁后解除
- 恢复系统正常运行
- 处理已发生的死锁
7.2.1 死锁的预防
死锁预防通过破坏死锁的四个必要条件之一,从根本上防止死锁的发生。
死锁预防策略:
1. 破坏互斥条件:
- 使用SPOOLing技术
- 将独占设备虚拟为共享设备
- 但某些资源必须互斥
2. 破坏请求和保持条件:
- 一次性分配所有资源
- 或申请前释放已有资源
- 最常用方法
3. 破坏不可抢占条件:
- 允许系统抢占资源
- 强制收回资源
- 但实现复杂
4. 破坏循环等待条件:
- 对资源编号
- 按编号顺序申请
- 常用方法
1. 破坏互斥条件
破坏互斥条件:
方法:
- 使用SPOOLing技术
- 将独占设备虚拟为共享设备
- 避免互斥访问
例子(打印机):
不使用SPOOLing:
- 打印机是独占设备
- 必须互斥访问
- 可能死锁
使用SPOOLing:
- 打印机虚拟为共享设备
- 多个进程可以"同时"使用
- 避免互斥,不会死锁
限制:
- 某些资源必须互斥
- 例如:写文件、修改共享数据
- 不能完全避免互斥
实际应用:
- 主要用于打印机等设备
- 不能解决所有死锁问题
- 需要结合其他方法
2. 破坏请求和保持条件
破坏请求和保持条件:
方法1:一次性分配(Allocate All):
- 进程必须一次性申请所有需要的资源
- 如果资源不足,不分配任何资源
- 等待所有资源都可用
优点:
- 简单有效
- 不会死锁
- 实现容易
缺点:
- 资源利用率低
- 进程可能长时间等待
- 可能饥饿
方法2:释放后申请(Release Before Request):
- 申请新资源前释放已有资源
- 然后重新申请所有资源
- 避免保持和等待
优点:
- 不会死锁
- 资源利用率较高
缺点:
- 可能丢失工作
- 需要保存和恢复状态
- 实现复杂
例子(一次性分配):
进程需要:打印机、磁带机、内存
不使用一次性分配:
1. 申请打印机(获得)
2. 申请磁带机(等待)
3. 可能死锁
使用一次性分配:
1. 同时申请打印机、磁带机、内存
2. 如果都可用,全部分配
3. 如果不可用,都不分配,等待
4. 不会死锁
3. 破坏不可抢占条件
破坏不可抢占条件:
方法:
- 允许系统强制收回资源
- 资源可以被抢占
- 分配给其他进程
实现:
1. 进程请求资源不可用时:
- 检查占用该资源的进程
- 如果该进程也在等待其他资源
- 强制收回资源,分配给请求进程
2. 被抢占的进程:
- 保存当前状态
- 释放被抢占的资源
- 重新申请所有资源
优点:
- 不会死锁
- 资源利用率较高
缺点:
1. 实现复杂:
- 需要保存和恢复状态
- 需要处理抢占
- 实现困难
2. 可能影响进程:
- 进程执行被中断
- 可能重复工作
- 影响性能
使用场景:
- CPU资源(可抢占)
- 某些内存资源
- 不适合所有资源
4. 破坏循环等待条件
破坏循环等待条件:
方法:
- 对资源进行编号
- 进程必须按编号顺序申请资源
- 不能先申请编号大的再申请编号小的
规则:
1. 资源编号:R1, R2, R3, ..., Rn
2. 进程申请顺序:必须从小到大
3. 不能违反顺序
例子:
资源编号:
R1: 打印机
R2: 磁带机
R3: 磁盘
正确顺序:
进程1:先R1,后R2 ✓
进程2:先R1,后R3 ✓
进程3:先R2,后R3 ✓
错误顺序:
进程1:先R2,后R1 ✗(违反顺序)
为什么不会死锁:
- 所有进程都按相同顺序申请
- 不会形成循环等待
- 例如:都先申请R1,只有一个能获得
- 其他等待,不会循环
优点:
1. 简单有效:
- 实现简单
- 效果明显
- 不会死锁
2. 资源利用率较高:
- 不需要一次性分配
- 可以逐步申请
- 利用率较高
缺点:
1. 资源编号困难:
- 需要合理编号
- 编号影响效率
- 可能不直观
2. 可能增加等待:
- 必须按顺序申请
- 可能增加等待时间
- 但不会死锁
实际应用:
- 最常用的预防方法
- 简单有效
- 广泛使用
死锁预防方法对比
死锁预防方法对比:
方法 效果 实现复杂度 资源利用率 实际使用
─────────────────────────────────────────────
破坏互斥 部分 简单 高 设备SPOOLing
破坏请求保持 好 中等 低 较少使用
破坏不可抢占 好 复杂 中 部分资源
破坏循环等待 好 简单 中高 最常用
选择:
- 最常用:破坏循环等待
- 简单有效
- 资源利用率较高
- 实现容易
7.2.2 死锁的避免
死锁避免不破坏死锁的必要条件,而是在资源分配时动态检查,避免系统进入可能导致死锁的状态。
死锁避免:
定义:
- 在资源分配时动态检查
- 判断分配后是否安全
- 只有安全才分配
- 避免进入不安全状态
特点:
- 不破坏必要条件
- 动态检查
- 需要知道资源需求
- 可能拒绝分配
与预防的区别:
预防:
- 破坏必要条件
- 静态方法
- 限制严格
避免:
- 不破坏条件
- 动态检查
- 更灵活
系统安全状态
系统安全状态是死锁避免的核心概念。
系统安全状态:
定义:
- 系统存在一个安全序列
- 按照该序列分配资源
- 所有进程都能完成
- 不会死锁
安全序列:
- 进程执行序列:P1, P2, ..., Pn
- 对每个进程Pi:
* Pi需要的资源 <= 当前可用资源 + 前面进程释放的资源
* Pi可以完成
* Pi释放资源
- 所有进程都能完成
安全状态 vs 不安全状态:
安全状态:
- 存在安全序列
- 不会死锁
- 可以继续分配资源
不安全状态:
- 不存在安全序列
- 可能死锁
- 不应该分配资源
注意:
- 不安全状态不一定是死锁
- 但可能导致死锁
- 应该避免
安全状态示例
安全状态示例:
系统状态:
资源总数:12
可用资源:3
进程:
P1: 已分配5,最大需求10,还需要5
P2: 已分配2,最大需求4,还需要2
P3: 已分配2,最大需求9,还需要7
检查安全序列:
序列1:P2 → P1 → P3
P2需要2,可用3,可以完成
完成后释放2,可用变为5
P1需要5,可用5,可以完成
完成后释放5,可用变为10
P3需要7,可用10,可以完成
安全序列存在,系统安全
序列2:P1 → P2 → P3
P1需要5,可用3,不能完成
不是安全序列
结论:
- 存在安全序列(P2 → P1 → P3)
- 系统处于安全状态
- 可以分配资源
银行家算法(Banker’s Algorithm)
银行家算法是死锁避免的经典算法,由Dijkstra提出。
银行家算法:
思想:
- 银行家(系统)有有限资金(资源)
- 客户(进程)申请贷款(资源)
- 银行家检查贷款后是否安全
- 只有安全才批准贷款
算法数据结构:
1. Available[m]:
- 可用资源向量
- Available[i] = 资源i的可用数量
2. Max[n][m]:
- 最大需求矩阵
- Max[i][j] = 进程i对资源j的最大需求
3. Allocation[n][m]:
- 分配矩阵
- Allocation[i][j] = 进程i已分配资源j的数量
4. Need[n][m]:
- 需求矩阵
- Need[i][j] = 进程i还需要资源j的数量
- Need[i][j] = Max[i][j] - Allocation[i][j]
银行家算法的安全性检查
银行家算法安全性检查:
算法:
1. 初始化:
Work = Available
Finish[i] = false (所有进程)
2. 查找进程:
找到进程i满足:
Finish[i] = false
Need[i] <= Work
如果找到,继续步骤3
如果找不到,转到步骤4
3. 假设进程i完成:
Work = Work + Allocation[i]
Finish[i] = true
转到步骤2
4. 检查:
如果所有Finish[i] = true
系统安全
否则
系统不安全
例子:
系统:3种资源,总数(10, 5, 7)
可用:(3, 3, 2)
进程:
P0: Allocation(0,1,0), Need(7,4,3), Max(7,5,3)
P1: Allocation(2,0,0), Need(1,2,2), Max(3,2,2)
P2: Allocation(3,0,2), Need(6,0,0), Max(9,0,2)
P3: Allocation(2,1,1), Need(0,1,1), Max(2,2,2)
P4: Allocation(0,0,2), Need(4,3,1), Max(4,3,3)
安全性检查:
Work = (3,3,2)
查找:P1的Need(1,2,2) <= Work(3,3,2) ✓
Work = (3,3,2) + (2,0,0) = (5,3,2)
Finish[1] = true
查找:P3的Need(0,1,1) <= Work(5,3,2) ✓
Work = (5,3,2) + (2,1,1) = (7,4,3)
Finish[3] = true
查找:P4的Need(4,3,1) <= Work(7,4,3) ✓
Work = (7,4,3) + (0,0,2) = (7,4,5)
Finish[4] = true
查找:P0的Need(7,4,3) <= Work(7,4,5) ✓
Work = (7,4,5) + (0,1,0) = (7,5,5)
Finish[0] = true
查找:P2的Need(6,0,0) <= Work(7,5,5) ✓
Work = (7,5,5) + (3,0,2) = (10,5,7)
Finish[2] = true
所有Finish = true,系统安全
安全序列:P1 → P3 → P4 → P0 → P2
银行家算法的资源分配
银行家算法资源分配:
当进程请求资源时:
1. 检查请求是否合法:
Request[i] <= Need[i]
Request[i] <= Available
如果不合法,拒绝
2. 试探性分配:
Available = Available - Request[i]
Allocation[i] = Allocation[i] + Request[i]
Need[i] = Need[i] - Request[i]
3. 安全性检查:
调用安全性检查算法
如果安全,正式分配
如果不安全,恢复分配,拒绝请求
例子:
进程P1请求资源(1,0,2)
步骤1:检查合法性
Request(1,0,2) <= Need[1](1,2,2) ✓
Request(1,0,2) <= Available(3,3,2) ✓
合法
步骤2:试探性分配
Available = (3,3,2) - (1,0,2) = (2,3,0)
Allocation[1] = (2,0,0) + (1,0,2) = (3,0,2)
Need[1] = (1,2,2) - (1,0,2) = (0,2,0)
步骤3:安全性检查
检查新状态是否安全
如果安全,正式分配
如果不安全,恢复,拒绝
银行家算法的优缺点
银行家算法优缺点:
优点:
1. 避免死锁:
- 动态检查
- 避免不安全状态
- 不会死锁
2. 资源利用率较高:
- 不限制资源申请顺序
- 允许逐步申请
- 利用率较高
3. 灵活:
- 动态适应
- 不需要预先知道所有需求
- 相对灵活
缺点:
1. 需要知道最大需求:
- 需要预先知道Max矩阵
- 实际难以预测
- 限制使用
2. 进程数受限:
- 算法复杂度O(m×n²)
- 进程数多时计算量大
- 性能影响
3. 可能拒绝合法请求:
- 即使资源足够
- 如果会导致不安全状态
- 也会拒绝
使用场景:
- 理论重要
- 实际系统较少使用
- 主要用于教学和研究
7.2.3 死锁的检测
死锁检测允许死锁发生,但定期检测系统是否出现死锁,如果发现死锁则处理。
死锁检测:
策略:
- 不预防死锁
- 允许死锁发生
- 定期检测死锁
- 发现后处理
优点:
- 不限制资源分配
- 资源利用率高
- 系统灵活性高
缺点:
- 死锁可能发生
- 需要检测和处理
- 可能影响系统
检测时机:
1. 定时检测:
- 定期运行检测算法
- 例如:每小时检测一次
2. 资源请求时检测:
- 每次资源请求时检测
- 及时发现死锁
3. CPU利用率低时检测:
- CPU利用率低可能表示死锁
- 此时检测
死锁检测算法
死锁检测算法通过资源分配图检测是否存在循环等待。
死锁检测算法:
数据结构:
1. Available[m]:可用资源向量
2. Allocation[n][m]:分配矩阵
3. Request[n][m]:请求矩阵(进程当前请求)
算法:
1. 初始化:
Work = Available
Finish[i] = false (如果Allocation[i] != 0)
Finish[i] = true (如果Allocation[i] == 0)
2. 查找进程:
找到进程i满足:
Finish[i] = false
Request[i] <= Work
如果找到,继续步骤3
如果找不到,转到步骤4
3. 假设进程i完成:
Work = Work + Allocation[i]
Finish[i] = true
转到步骤2
4. 检查:
如果所有Finish[i] = true
无死锁
否则
存在死锁
Finish[i] = false的进程是死锁进程
原理:
- 模拟资源分配
- 可以完成的进程标记为完成
- 无法完成的进程可能是死锁进程
死锁检测示例
死锁检测示例:
系统状态:
资源:A(7个), B(2个), C(6个)
可用:A(0), B(0), C(0)
进程:
P0: Allocation(0,1,0), Request(0,0,0)
P1: Allocation(2,0,0), Request(2,0,2)
P2: Allocation(3,0,3), Request(0,0,0)
P3: Allocation(2,1,1), Request(1,0,0)
P4: Allocation(0,0,2), Request(0,0,2)
检测过程:
Work = (0,0,0)
步骤1:初始化Finish
P0: Allocation != 0, Finish = false
P1: Allocation != 0, Finish = false
P2: Allocation != 0, Finish = false
P3: Allocation != 0, Finish = false
P4: Allocation != 0, Finish = false
步骤2:查找进程
P0: Request(0,0,0) <= Work(0,0,0) ✓
Work = (0,0,0) + (0,1,0) = (0,1,0)
Finish[0] = true
P2: Request(0,0,0) <= Work(0,1,0) ✓
Work = (0,1,0) + (3,0,3) = (3,1,3)
Finish[2] = true
P3: Request(1,0,0) <= Work(3,1,3) ✓
Work = (3,1,3) + (2,1,1) = (5,2,4)
Finish[3] = true
P1: Request(2,0,2) <= Work(5,2,4) ✓
Work = (5,2,4) + (2,0,0) = (7,2,4)
Finish[1] = true
P4: Request(0,0,2) <= Work(7,2,4) ✓
Work = (7,2,4) + (0,0,2) = (7,2,6)
Finish[4] = true
所有Finish = true,无死锁
死锁检测的复杂度
死锁检测复杂度:
时间复杂度:
- O(m × n²)
- m是资源类型数
- n是进程数
优化:
- 使用资源分配图
- 检测图中是否有环
- 可以使用DFS检测环
- 复杂度可以降低
检测频率:
- 频率高:及时发现,但开销大
- 频率低:开销小,但可能延迟发现
- 需要平衡
实际系统:
- 通常定时检测
- 或资源请求时检测
- 根据系统负载调整
7.2.4 死锁的解除
死锁解除是在检测到死锁后,采取措施解除死锁,恢复系统正常运行。
死锁解除:
前提:
- 已经检测到死锁
- 需要解除死锁
- 恢复系统运行
方法:
1. 终止进程
2. 抢占资源
3. 回滚操作
1. 终止进程
终止进程是解除死锁最直接的方法。
终止进程:
方法:
1. 终止所有死锁进程:
- 简单直接
- 但损失大
- 所有工作丢失
2. 逐个终止进程:
- 终止一个进程
- 检查是否解除死锁
- 如果未解除,继续终止
- 直到死锁解除
终止顺序选择:
1. 优先级:
- 终止优先级低的进程
- 保留重要进程
2. 执行时间:
- 终止执行时间短的进程
- 减少损失
3. 资源占用:
- 终止占用资源少的进程
- 快速释放资源
4. 完成度:
- 终止完成度低的进程
- 减少重复工作
例子:
死锁进程:P1, P2, P3
选择终止P2(优先级最低):
1. 终止P2
2. P2释放资源
3. P1和P3获得资源
4. 死锁解除
5. P1和P3继续执行
2. 抢占资源
抢占资源是从死锁进程中强制收回资源,解除死锁。
抢占资源:
方法:
1. 选择被抢占的进程:
- 选择优先级低的进程
- 或选择容易恢复的进程
2. 强制收回资源:
- 从选中进程收回资源
- 分配给等待的进程
3. 进程恢复:
- 被抢占的进程回滚
- 重新申请资源
- 继续执行
选择被抢占进程的原则:
1. 优先级:
- 选择优先级低的进程
- 保留重要进程
2. 资源占用:
- 选择占用资源少的进程
- 快速释放资源
3. 执行时间:
- 选择执行时间短的进程
- 减少回滚损失
4. 回滚成本:
- 选择回滚成本低的进程
- 容易恢复
例子:
死锁:P1等待P2的资源,P2等待P1的资源
抢占P2的资源:
1. 强制收回P2的资源
2. 分配给P1
3. P1继续执行
4. P2回滚,重新申请资源
5. 死锁解除
3. 回滚操作
回滚操作是将进程回滚到死锁发生前的状态。
回滚操作:
方法:
1. 检查点(Checkpoint):
- 定期保存进程状态
- 记录检查点
- 用于回滚
2. 回滚到检查点:
- 将进程状态恢复到检查点
- 释放检查点后获得的资源
- 重新执行
3. 饥饿问题:
- 可能同一进程反复被回滚
- 需要防止饥饿
- 记录回滚次数
检查点机制:
- 系统定期创建检查点
- 保存进程状态
- 包括:寄存器、内存、文件状态等
- 用于故障恢复和死锁解除
回滚成本:
- 需要保存和恢复状态
- 可能重复执行
- 有性能开销
- 需要权衡
死锁解除方法对比
死锁解除方法对比:
方法 效果 实现复杂度 损失 适用场景
─────────────────────────────────────────────
终止所有进程 好 简单 大 紧急情况
逐个终止进程 好 中等 中 常用
抢占资源 好 复杂 中 有检查点
回滚操作 好 复杂 小 有检查点系统
选择:
- 通常使用逐个终止进程
- 简单有效
- 损失可接受
- 实现相对简单
死锁处理方法总结
死锁处理方法总结:
方法 策略 优点 缺点 使用场景
─────────────────────────────────────────────────────
预防 破坏条件 不会死锁 限制严格 严格系统
避免 动态检查 灵活 需要预测 理论重要
检测和解除 允许发生 灵活 可能发生 实际系统常用
现代系统:
- 主要使用检测和解除
- 结合预防(资源有序分配)
- 平衡性能和安全性
- 实际系统常用组合方法
死锁处理的综合策略
死锁处理综合策略:
实际系统策略:
1. 预防(部分):
- 对某些资源使用有序分配
- 避免明显的死锁
2. 检测:
- 定期检测死锁
- 或资源请求时检测
- 及时发现
3. 解除:
- 检测到死锁后解除
- 终止部分进程
- 恢复系统
组合优势:
- 预防减少死锁概率
- 检测及时发现
- 解除恢复系统
- 平衡各种因素
现代操作系统:
- Linux:主要使用检测和解除
- Windows:类似策略
- 结合预防措施
- 实际效果良好
八、操作系统安全
操作系统安全是操作系统设计和使用中的重要问题。随着计算机网络的普及和信息安全威胁的增加,操作系统安全变得越来越重要。操作系统需要提供安全机制来保护系统资源、用户数据和系统完整性。
8.1 操作系统安全概述
操作系统安全是指保护操作系统及其管理的资源免受未授权访问、恶意攻击和意外破坏。操作系统安全涉及多个方面,包括身份认证、访问控制、数据加密、审计等。
8.1.1 安全威胁
操作系统面临多种安全威胁,了解这些威胁是设计安全机制的基础。
安全威胁分类:
1. 未授权访问:
- 非法用户访问系统
- 未授权访问资源
- 权限提升
2. 恶意软件:
- 病毒、蠕虫、木马
- 恶意代码执行
- 系统破坏
3. 数据泄露:
- 敏感数据泄露
- 隐私信息泄露
- 数据窃取
4. 拒绝服务:
- 系统资源耗尽
- 服务不可用
- 系统崩溃
5. 完整性破坏:
- 数据被篡改
- 系统文件被修改
- 配置被改变
1. 未授权访问
未授权访问:
定义:
- 未经授权的用户或进程访问系统资源
- 绕过访问控制机制
- 获得不应有的权限
威胁类型:
1. 非法登录:
- 使用他人账户登录
- 破解密码
- 利用系统漏洞
2. 权限提升:
- 普通用户获得管理员权限
- 利用系统漏洞
- 执行特权操作
3. 未授权资源访问:
- 访问其他用户的文件
- 访问系统文件
- 访问受限资源
例子:
- 密码破解:暴力破解用户密码
- 缓冲区溢出:利用程序漏洞获得权限
- 社会工程:欺骗用户获得密码
- 后门程序:留下后门供以后访问
防护:
- 强密码策略
- 访问控制
- 最小权限原则
- 安全审计
2. 恶意软件
恶意软件:
定义:
- 恶意设计的软件
- 破坏系统或窃取信息
- 未经用户同意安装
类型:
1. 病毒(Virus):
- 自我复制的恶意代码
- 需要宿主程序
- 传播和破坏
2. 蠕虫(Worm):
- 独立的恶意程序
- 通过网络传播
- 自我复制
3. 木马(Trojan):
- 伪装成正常程序
- 执行恶意功能
- 欺骗用户
4. 间谍软件(Spyware):
- 收集用户信息
- 监控用户行为
- 隐私侵犯
5. 勒索软件(Ransomware):
- 加密用户文件
- 要求赎金
- 数据勒索
威胁:
- 系统破坏
- 数据丢失
- 隐私泄露
- 资源消耗
防护:
- 防病毒软件
- 防火墙
- 系统更新
- 用户教育
3. 数据泄露
数据泄露:
定义:
- 敏感数据被未授权访问
- 数据被窃取或泄露
- 隐私信息暴露
泄露途径:
1. 网络攻击:
- 网络监听
- 中间人攻击
- 数据包捕获
2. 存储设备:
- 硬盘丢失
- 备份泄露
- 设备丢弃
3. 内部泄露:
- 内部人员泄露
- 权限滥用
- 误操作
4. 应用程序漏洞:
- SQL注入
- 跨站脚本
- 应用程序缺陷
影响:
- 隐私侵犯
- 经济损失
- 声誉损害
- 法律责任
防护:
- 数据加密
- 访问控制
- 数据备份
- 安全传输
4. 拒绝服务攻击(DoS)
拒绝服务攻击:
定义:
- 使系统或服务不可用
- 耗尽系统资源
- 阻止合法用户访问
类型:
1. 资源耗尽:
- CPU资源耗尽
- 内存资源耗尽
- 网络带宽耗尽
- 磁盘空间耗尽
2. 服务崩溃:
- 利用漏洞使服务崩溃
- 系统重启
- 服务不可用
3. 分布式拒绝服务(DDoS):
- 多个攻击源同时攻击
- 大规模攻击
- 难以防御
例子:
- 洪水攻击:发送大量数据包
- 死亡之ping:发送超大ping包
- SYN洪水:耗尽连接资源
- 资源耗尽:创建大量进程
影响:
- 服务不可用
- 系统性能下降
- 用户体验差
- 经济损失
防护:
- 流量限制
- 防火墙
- 入侵检测
- 资源限制
5. 完整性破坏
完整性破坏:
定义:
- 数据或系统被未授权修改
- 文件被篡改
- 配置被改变
威胁类型:
1. 数据篡改:
- 文件内容被修改
- 数据库被篡改
- 日志被删除
2. 系统文件修改:
- 系统文件被替换
- 配置文件被修改
- 可执行文件被感染
3. 配置改变:
- 安全配置被改变
- 访问控制被修改
- 系统设置被改变
影响:
- 系统不可信
- 数据不可靠
- 安全机制失效
- 系统不稳定
防护:
- 文件完整性检查
- 数字签名
- 访问控制
- 配置管理
8.1.2 安全机制
操作系统提供多种安全机制来应对安全威胁,保护系统安全。
安全机制:
1. 身份认证(Authentication)
2. 访问控制(Access Control)
3. 加密(Encryption)
4. 审计(Audit)
5. 防火墙(Firewall)
6. 入侵检测(Intrusion Detection)
7. 安全内核(Security Kernel)
1. 身份认证
身份认证:
定义:
- 验证用户身份
- 确认用户是谁
- 防止未授权访问
认证方式:
1. 密码认证:
- 用户名和密码
- 最常用方式
- 需要强密码
2. 生物特征认证:
- 指纹识别
- 面部识别
- 虹膜识别
3. 智能卡认证:
- 物理卡片
- 芯片存储密钥
- 双因素认证
4. 多因素认证:
- 组合多种方式
- 提高安全性
- 例如:密码+短信验证码
实现:
- 登录时验证
- 会话管理
- 定期重新认证
- 安全存储凭证
2. 访问控制
访问控制:
定义:
- 控制用户对资源的访问
- 决定谁可以访问什么
- 防止未授权访问
类型:
1. 自主访问控制(DAC):
- 资源所有者决定访问权限
- 灵活但可能不安全
2. 强制访问控制(MAC):
- 系统强制实施访问规则
- 安全但不够灵活
3. 基于角色的访问控制(RBAC):
- 基于角色分配权限
- 平衡安全性和灵活性
实现:
- 访问控制列表(ACL)
- 权限位(Unix)
- 安全标签(MAC)
- 角色权限(RBAC)
3. 加密
加密:
定义:
- 将数据转换为不可读形式
- 只有授权用户可以解密
- 保护数据机密性
类型:
1. 对称加密:
- 加密和解密使用相同密钥
- 速度快
- 密钥管理困难
2. 非对称加密:
- 加密和解密使用不同密钥
- 密钥管理容易
- 速度较慢
3. 哈希函数:
- 单向函数
- 用于数据完整性
- 密码存储
应用:
- 数据加密存储
- 网络传输加密
- 密码存储
- 数字签名
4. 审计
审计:
定义:
- 记录系统活动
- 追踪用户行为
- 检测安全事件
审计内容:
1. 登录和注销
2. 文件访问
3. 权限变更
4. 系统配置修改
5. 异常行为
审计日志:
- 记录时间、用户、操作
- 安全存储
- 防止篡改
- 定期分析
用途:
- 安全事件调查
- 合规性检查
- 异常检测
- 取证分析
5. 防火墙
防火墙:
定义:
- 控制网络流量
- 阻止未授权访问
- 保护系统边界
类型:
1. 包过滤防火墙:
- 检查数据包
- 根据规则过滤
- 简单高效
2. 状态检测防火墙:
- 跟踪连接状态
- 更智能的过滤
- 性能较好
3. 应用层防火墙:
- 检查应用层数据
- 深度包检测
- 安全性高
功能:
- 阻止未授权访问
- 允许合法流量
- 记录网络活动
- 防止攻击
6. 入侵检测
入侵检测:
定义:
- 检测系统入侵行为
- 识别攻击模式
- 及时响应
类型:
1. 基于签名的检测:
- 匹配已知攻击模式
- 准确但只能检测已知攻击
2. 基于异常的检测:
- 检测异常行为
- 可以发现未知攻击
- 但可能误报
实现:
- 实时监控
- 日志分析
- 模式匹配
- 告警机制
8.1.3 安全模型
安全模型是描述系统安全策略的抽象框架,用于指导安全机制的设计和实现。
安全模型:
1. 访问矩阵模型
2. Bell-LaPadula模型
3. Biba模型
4. Clark-Wilson模型
5. 中国墙模型
1. 访问矩阵模型
访问矩阵模型:
定义:
- 用矩阵表示访问权限
- 行表示主体(用户、进程)
- 列表示客体(文件、资源)
- 矩阵元素表示权限
结构:
文件1 文件2 文件3
用户1 rw- r-- ---
用户2 r-- rw- r--
用户3 --- --- rwx
权限:
r: 读
w: 写
x: 执行
-: 无权限
实现:
- 访问控制列表(ACL):按列存储
- 能力列表(Capability):按行存储
- 简化实现:权限位(Unix)
特点:
- 直观
- 灵活
- 但矩阵可能很大
- 需要优化存储
2. Bell-LaPadula模型
Bell-LaPadula模型:
定义:
- 多级安全模型
- 用于军事和政府系统
- 保护数据机密性
安全级别:
- 公开(Unclassified)
- 内部(Confidential)
- 秘密(Secret)
- 绝密(Top Secret)
规则:
1. 简单安全属性(下读):
- 主体只能读同级或更低级的客体
- 不能向上读
2. *-属性(上写):
- 主体只能写同级或更高级的客体
- 不能向下写
目的:
- 防止信息泄露
- 保护机密数据
- 强制访问控制
例子:
绝密用户:
- 可以读:绝密、秘密、内部、公开
- 可以写:绝密
- 不能写:秘密、内部、公开(防止泄露)
公开用户:
- 可以读:公开
- 可以写:公开
- 不能读:内部、秘密、绝密
3. Biba模型
Biba模型:
定义:
- 完整性模型
- 保护数据完整性
- 与Bell-LaPadula相对
完整性级别:
- 高完整性
- 中完整性
- 低完整性
规则:
1. 简单完整性属性(下写):
- 主体只能写同级或更低级的客体
- 不能向上写
2. 完整性*属性(上读):
- 主体只能读同级或更高级的客体
- 不能向下读
目的:
- 防止低完整性数据污染高完整性数据
- 保护系统完整性
- 防止恶意代码传播
例子:
高完整性程序:
- 可以读:高完整性数据
- 可以写:高完整性、中完整性、低完整性
- 不能读:低完整性数据(防止污染)
低完整性程序:
- 可以读:低完整性、中完整性、高完整性
- 可以写:低完整性
- 不能写:高完整性数据(防止污染)
4. Clark-Wilson模型
Clark-Wilson模型:
定义:
- 商业完整性模型
- 保护数据完整性
- 适合商业应用
概念:
1. 受约束数据项(CDI):
- 需要保护的数据
- 只能通过转换过程修改
2. 不受约束数据项(UDI):
- 不需要保护的数据
- 可以自由修改
3. 完整性验证过程(IVP):
- 验证数据完整性
- 检查数据是否正确
4. 转换过程(TP):
- 修改数据的唯一方式
- 必须经过验证
规则:
1. 所有转换过程必须经过认证
2. 只有认证用户才能执行转换过程
3. 必须维护审计日志
4. 数据必须经过完整性验证
应用:
- 数据库系统
- 金融系统
- 商业应用
5. 中国墙模型
中国墙模型:
定义:
- 利益冲突模型
- 防止利益冲突
- 用于咨询、审计等行业
概念:
1. 利益冲突类(COI):
- 相互竞争的公司属于同一COI
- 不能同时访问
2. 公司数据集(CD):
- 特定公司的数据
- 属于某个COI
规则:
1. 简单中国墙属性:
- 用户访问一个CD后
- 不能访问同一COI的其他CD
2. 自主安全属性:
- 用户可以访问任何CD
- 只要不违反简单中国墙属性
目的:
- 防止利益冲突
- 保护客户隐私
- 维护职业道德
例子:
COI1:{公司A, 公司B}(竞争对手)
COI2:{公司C, 公司D}(竞争对手)
用户访问公司A的数据后:
- 可以访问:公司C、公司D
- 不能访问:公司B(同一COI)
安全模型对比
安全模型对比:
模型 目标 应用场景 特点
─────────────────────────────────────────
访问矩阵 通用 通用系统 灵活
Bell-LaPadula 机密性 军事政府 多级安全
Biba 完整性 系统保护 完整性保护
Clark-Wilson 完整性 商业应用 商业完整性
中国墙 利益冲突 咨询审计 冲突避免
选择:
- 根据应用需求选择
- 可以组合使用
- 实际系统可能使用多种模型
8.2 访问控制
访问控制是操作系统安全的核心机制,决定谁可以访问什么资源,以及可以执行什么操作。访问控制是防止未授权访问的主要手段。
8.2.1 访问控制模型
访问控制模型定义了如何表示和控制访问权限。
访问控制模型:
基本概念:
1. 主体(Subject):
- 请求访问的实体
- 用户、进程、程序
- 主动访问者
2. 客体(Object):
- 被访问的资源
- 文件、目录、设备
- 被动被访问者
3. 访问权限(Access Right):
- 允许的操作
- 读、写、执行等
- 权限集合
访问控制三要素:
- 主体(谁)
- 客体(什么)
- 权限(如何)
访问矩阵模型
访问矩阵模型:
定义:
- 用矩阵表示所有访问权限
- 行:主体
- 列:客体
- 元素:权限集合
矩阵示例:
文件1 文件2 文件3 打印机
用户1 {r,w} {r} {} {}
用户2 {r} {r,w} {r} {}
用户3 {} {} {r,w,x} {w}
进程1 {r} {r} {} {}
实现方式:
1. 访问控制列表(ACL):
- 按列存储
- 每个客体一个列表
- 列出可以访问的主体和权限
2. 能力列表(Capability):
- 按行存储
- 每个主体一个列表
- 列出可以访问的客体和权限
3. 权限位(Permission Bits):
- 简化实现
- Unix使用
- 所有者、组、其他三类
8.2.2 自主访问控制(DAC)
自主访问控制(Discretionary Access Control,DAC)是最常用的访问控制方式,资源所有者决定访问权限。
自主访问控制(DAC):
定义:
- 资源所有者自主决定访问权限
- 可以授予或撤销权限
- 灵活但可能不安全
特点:
1. 自主性:
- 所有者完全控制
- 可以自由授权
- 灵活性高
2. 传递性:
- 权限可以传递
- 用户A授权给用户B
- 用户B可以授权给用户C
3. 可撤销:
- 所有者可以撤销权限
- 动态管理
- 灵活控制
实现:
- 访问控制列表(ACL)
- 权限位(Unix)
- 文件所有者控制
Unix权限位
Unix权限位:
结构:
-rwxr-xr--
所有者 组 其他
权限位:
r: 读(4)
w: 写(2)
x: 执行(1)
权限值:
rwx = 4+2+1 = 7
r-x = 4+0+1 = 5
r-- = 4+0+0 = 4
例子:
-rwxr-xr-- 1 user group 1024 file.txt
所有者(user):读写执行(rwx)
组(group):读执行(r-x)
其他:只读(r--)
权限设置:
chmod 754 file.txt
所有者:7(rwx)
组:5(r-x)
其他:4(r--)
目录权限:
r: 列出目录内容
w: 创建/删除文件
x: 进入目录
访问控制列表(ACL)
访问控制列表(ACL):
定义:
- 为每个客体维护访问列表
- 列出可以访问的主体和权限
- 比权限位更灵活
结构:
文件:file.txt
ACL:
user1: rwx
user2: r-x
group1: r--
others: ---
优势:
- 可以为特定用户设置权限
- 比权限位更细粒度
- 更灵活
例子:
文件:report.txt
ACL:
owner: rwx
alice: rw-
bob: r--
group: r--
others: ---
说明:
- 所有者:完全控制
- alice:读写
- bob:只读
- 组:只读
- 其他:无权限
DAC的优缺点
DAC优缺点:
优点:
1. 灵活性高:
- 所有者完全控制
- 可以精细授权
- 适应各种需求
2. 实现简单:
- 概念简单
- 实现容易
- 广泛使用
3. 用户友好:
- 用户容易理解
- 操作简单
- 适合个人系统
缺点:
1. 安全性较低:
- 所有者可能错误授权
- 权限可能传递
- 难以控制
2. 管理困难:
- 权限分散
- 难以统一管理
- 可能不一致
3. 不适合高安全环境:
- 不能强制安全策略
- 用户可能绕过
- 需要更高安全级别
使用场景:
- 个人计算机
- 一般服务器
- 需要灵活性的系统
- Unix/Linux系统
8.2.3 强制访问控制(MAC)
强制访问控制(Mandatory Access Control,MAC)由系统强制实施访问规则,用户不能改变。
强制访问控制(MAC):
定义:
- 系统强制实施访问规则
- 用户不能改变规则
- 安全但不够灵活
特点:
1. 强制性:
- 系统强制实施
- 用户不能绕过
- 安全性高
2. 不可改变:
- 用户不能改变规则
- 只能由管理员设置
- 统一管理
3. 多级安全:
- 支持安全级别
- 机密、秘密、绝密等
- 防止信息泄露
实现:
- 安全标签
- 安全级别
- 系统强制检查
安全标签
安全标签:
定义:
- 为主体 and 客体分配安全标签
- 标签包含安全级别
- 系统根据标签决定访问
安全级别:
- 公开(Unclassified)
- 内部(Confidential)
- 秘密(Secret)
- 绝密(Top Secret)
标签结构:
主体标签:
- 用户:秘密级别
- 可以访问:秘密及以下级别
客体标签:
- 文件:秘密级别
- 只能被:秘密及以上级别访问
访问规则:
1. 下读规则:
- 主体只能读同级或更低级的客体
- 防止信息泄露
2. 上写规则:
- 主体只能写同级或更高级的客体
- 防止信息降级
例子:
用户(秘密级别):
- 可以读:公开、内部、秘密文件
- 可以写:秘密、绝密文件
- 不能读:绝密文件
- 不能写:公开、内部文件
MAC的实现
MAC实现:
SELinux(Security-Enhanced Linux):
- Linux的MAC实现
- 使用安全上下文
- 强制访问控制
安全上下文:
用户:角色:类型:级别
例子:
user_u:object_r:file_t:s0
system_u:system_r:httpd_t:s0
类型强制(TE):
- 基于类型的访问控制
- 定义类型和规则
- 系统强制实施
规则示例:
allow httpd_t file_t:file read;
allow httpd_t file_t:file write;
说明:
- httpd_t类型可以读写file_t类型
- 系统强制实施
- 用户不能改变
MAC的优缺点
MAC优缺点:
优点:
1. 安全性高:
- 系统强制实施
- 用户不能绕过
- 适合高安全环境
2. 统一管理:
- 集中管理策略
- 一致的安全策略
- 便于维护
3. 防止信息泄露:
- 多级安全
- 强制规则
- 有效防护
缺点:
1. 灵活性低:
- 用户不能改变
- 配置复杂
- 可能影响使用
2. 实现复杂:
- 需要定义策略
- 配置困难
- 需要专业知识
3. 性能影响:
- 每次访问都检查
- 可能影响性能
- 需要优化
使用场景:
- 军事系统
- 政府系统
- 高安全环境
- 需要强制策略的系统
8.2.4 基于角色的访问控制(RBAC)
基于角色的访问控制(Role-Based Access Control,RBAC)通过角色来管理权限,是DAC和MAC的折中方案。
基于角色的访问控制(RBAC):
定义:
- 用户被分配角色
- 角色有权限
- 用户通过角色获得权限
核心概念:
1. 用户(User):
- 系统的使用者
- 被分配角色
2. 角色(Role):
- 权限的集合
- 代表工作职能
- 例如:管理员、编辑、读者
3. 权限(Permission):
- 对资源的操作
- 例如:读文件、写文件
关系:
用户 → 角色 → 权限 → 资源
RBAC模型
RBAC模型:
基本模型(RBAC0):
用户 → 角色 → 权限
用户被分配角色
角色有权限集合
用户通过角色获得权限
层次模型(RBAC1):
角色可以有层次关系
高级角色继承低级角色权限
例子:
管理员角色
↓ 继承
编辑角色
↓ 继承
读者角色
约束模型(RBAC2):
添加约束条件
例如:
- 互斥角色:用户不能同时有两个互斥角色
- 基数约束:角色最多N个用户
- 时间约束:角色在特定时间有效
统一模型(RBAC3):
结合层次和约束
完整的RBAC模型
RBAC示例
RBAC示例:
角色定义:
管理员(Admin):
- 所有权限
- 系统管理
编辑(Editor):
- 读写文件
- 修改内容
读者(Reader):
- 只读文件
- 查看内容
用户分配:
用户1:管理员角色
用户2:编辑角色
用户3:读者角色
权限检查:
用户2访问文件:
1. 检查用户2的角色:编辑
2. 检查编辑角色的权限:读写
3. 允许读写操作
角色层次:
高级管理员
↓ 继承
管理员
↓ 继承
编辑
↓ 继承
读者
约束示例:
互斥角色:
- 会计和出纳不能同时担任
- 防止利益冲突
基数约束:
- 管理员角色最多3人
- 防止权限过度集中
RBAC的优势
RBAC优势:
1. 简化管理:
- 按角色管理权限
- 不需要为每个用户设置
- 管理简单
2. 灵活性:
- 可以灵活分配角色
- 可以组合角色
- 适应各种需求
3. 安全性:
- 最小权限原则
- 角色分离
- 约束控制
4. 可扩展性:
- 容易添加新角色
- 容易修改权限
- 适应变化
5. 符合实际:
- 符合组织架构
- 符合工作职能
- 易于理解
使用场景:
- 企业系统
- 数据库系统
- Web应用
- 现代操作系统
访问控制方法对比
访问控制方法对比:
方法 安全性 灵活性 管理复杂度 适用场景
─────────────────────────────────────────────
DAC 低 高 简单 个人系统
MAC 高 低 复杂 高安全系统
RBAC 中高 中高 中等 企业系统
现代系统:
- 通常组合使用
- 基础使用DAC
- 高安全使用MAC
- 企业使用RBAC
- 根据需求选择
8.3 安全机制
安全机制是操作系统实现安全的具体技术和方法。本节详细介绍身份认证、加密技术、审计机制和安全内核等核心安全机制。
8.3.1 身份认证
身份认证(Authentication)是验证用户身份的过程,是系统安全的第一道防线。
身份认证:
定义:
- 验证用户身份
- 确认用户是谁
- 防止未授权访问
认证要素:
1. 你知道什么(Something you know):
- 密码
- PIN码
- 安全问题
2. 你拥有什么(Something you have):
- 智能卡
- 令牌
- 手机
3. 你是什么(Something you are):
- 指纹
- 面部
- 虹膜
单因素认证 vs 多因素认证:
- 单因素:只用一种方式(如密码)
- 多因素:组合多种方式(如密码+短信)
- 多因素更安全
1. 密码认证
密码认证:
定义:
- 最常用的认证方式
- 用户名和密码
- 简单但可能不安全
密码安全:
1. 强密码要求:
- 足够长度(至少8位)
- 包含大小写字母
- 包含数字和特殊字符
- 避免常见密码
2. 密码存储:
- 不能明文存储
- 使用哈希函数
- 加盐(Salt)处理
3. 密码策略:
- 定期更换
- 不能重复使用
- 复杂度要求
- 锁定机制
密码哈希:
存储:hash(password + salt)
登录时:
1. 用户输入密码
2. 系统加盐并哈希
3. 与存储的哈希比较
4. 匹配则认证成功
例子:
密码:MyPass123
盐:random_salt_123
哈希:SHA256(MyPass123 + random_salt_123)
存储:hash值和盐值
2. 生物特征认证
生物特征认证:
定义:
- 使用生物特征识别用户
- 指纹、面部、虹膜等
- 难以伪造
类型:
1. 指纹识别:
- 最常用
- 准确度高
- 成本低
2. 面部识别:
- 方便使用
- 技术成熟
- 广泛应用
3. 虹膜识别:
- 准确度最高
- 难以伪造
- 成本较高
4. 声纹识别:
- 语音特征
- 方便但准确度较低
特点:
优点:
- 难以伪造
- 方便使用
- 不需要记忆
缺点:
- 可能被复制
- 设备成本
- 隐私问题
使用场景:
- 手机解锁
- 门禁系统
- 高安全系统
3. 智能卡和令牌
智能卡和令牌:
智能卡:
- 物理卡片
- 内置芯片
- 存储密钥或证书
工作过程:
1. 用户插入智能卡
2. 系统读取卡片信息
3. 用户输入PIN码
4. 验证PIN和卡片
5. 认证成功
令牌(Token):
- 硬件或软件设备
- 生成动态密码
- 时间同步或事件同步
动态密码:
- 每60秒变化一次
- 或每次使用后变化
- 防止重放攻击
双因素认证:
1. 密码(你知道什么)
2. 智能卡/令牌(你拥有什么)
3. 组合使用,更安全
使用场景:
- 企业系统
- 银行系统
- 高安全环境
4. 多因素认证(MFA)
多因素认证:
定义:
- 组合多种认证方式
- 提高安全性
- 即使一种被破解,仍有保护
常见组合:
1. 密码 + 短信验证码:
- 你知道什么 + 你拥有什么
- 常用组合
2. 密码 + 智能卡:
- 你知道什么 + 你拥有什么
- 企业常用
3. 密码 + 生物特征:
- 你知道什么 + 你是什么
- 高安全系统
4. 密码 + 短信 + 生物特征:
- 三种方式组合
- 最高安全性
工作过程:
1. 用户输入密码
2. 系统发送验证码到手机
3. 用户输入验证码
4. 验证通过,认证成功
优势:
- 安全性高
- 即使密码泄露,仍有保护
- 适合重要系统
使用场景:
- 在线银行
- 企业系统
- 云服务
- 现代系统广泛使用
8.3.2 加密技术
加密技术是保护数据机密性的核心技术,将数据转换为不可读形式,只有授权用户可以解密。
加密技术:
目标:
- 保护数据机密性
- 防止数据泄露
- 保护传输和存储
类型:
1. 对称加密
2. 非对称加密
3. 哈希函数
4. 数字签名
1. 对称加密
对称加密:
定义:
- 加密和解密使用相同密钥
- 速度快
- 适合大量数据
算法:
1. DES(Data Encryption Standard):
- 56位密钥
- 已不安全
- 被淘汰
2. AES(Advanced Encryption Standard):
- 128/192/256位密钥
- 现代标准
- 广泛使用
3. 3DES:
- DES的改进
- 三重加密
- 仍在使用
工作过程:
1. 发送方:
- 明文 + 密钥 → 加密算法 → 密文
2. 传输:
- 密文传输
3. 接收方:
- 密文 + 密钥 → 解密算法 → 明文
密钥管理:
- 密钥必须安全传输
- 密钥分发困难
- 需要安全通道
使用场景:
- 文件加密
- 数据库加密
- 大量数据加密
2. 非对称加密
非对称加密:
定义:
- 加密和解密使用不同密钥
- 公钥和私钥
- 密钥管理容易
密钥对:
1. 公钥(Public Key):
- 可以公开
- 用于加密
- 任何人都可以获得
2. 私钥(Private Key):
- 必须保密
- 用于解密
- 只有拥有者知道
算法:
1. RSA:
- 最常用
- 基于大数分解
- 安全性高
2. ECC(椭圆曲线加密):
- 密钥短
- 效率高
- 现代应用
工作过程:
1. 接收方生成密钥对
2. 公钥公开,私钥保密
3. 发送方用公钥加密
4. 接收方用私钥解密
优势:
- 密钥管理容易
- 不需要安全通道传输密钥
- 适合密钥交换
缺点:
- 速度慢
- 不适合大量数据
使用场景:
- 密钥交换
- 数字签名
- 小量数据加密
3. 混合加密
混合加密:
定义:
- 结合对称和非对称加密
- 发挥各自优势
- 实际系统常用
工作过程:
1. 使用非对称加密交换对称密钥
2. 使用对称加密加密数据
3. 传输密文和加密的密钥
例子(HTTPS):
1. 客户端和服务器建立连接
2. 服务器发送公钥
3. 客户端生成对称密钥
4. 用服务器公钥加密对称密钥
5. 发送加密的对称密钥
6. 服务器用私钥解密获得对称密钥
7. 使用对称密钥加密通信
优势:
- 结合两种加密的优点
- 密钥管理容易
- 性能好
- 安全性高
使用场景:
- HTTPS
- VPN
- 安全通信
- 现代系统广泛使用
4. 哈希函数
哈希函数:
定义:
- 单向函数
- 将任意长度数据映射为固定长度
- 不可逆
特点:
1. 单向性:
- 从哈希值不能恢复原数据
- 只能正向计算
2. 确定性:
- 相同输入产生相同输出
- 可重复
3. 雪崩效应:
- 输入微小变化,输出大变化
- 难以预测
4. 抗碰撞:
- 难以找到两个输入产生相同输出
- 安全性保证
算法:
1. MD5:
- 128位输出
- 已不安全
- 不推荐使用
2. SHA-1:
- 160位输出
- 已不安全
- 不推荐使用
3. SHA-256:
- 256位输出
- 现代标准
- 广泛使用
应用:
1. 密码存储:
- 存储密码哈希值
- 不存储明文密码
2. 数据完整性:
- 计算数据哈希值
- 验证数据是否被篡改
3. 数字签名:
- 对哈希值签名
- 提高效率
5. 数字签名
数字签名:
定义:
- 证明数据来源和完整性
- 不可否认性
- 类似手写签名
工作过程:
1. 发送方:
- 计算数据哈希值
- 用私钥加密哈希值
- 发送数据和签名
2. 接收方:
- 计算数据哈希值
- 用公钥解密签名
- 比较两个哈希值
- 匹配则验证成功
特点:
- 证明数据来源
- 证明数据完整性
- 不可否认
- 防止篡改
应用:
- 软件签名
- 电子合同
- 安全通信
- 证书认证
8.3.3 审计机制
审计机制记录系统活动,追踪用户行为,用于安全事件调查和合规性检查。
审计机制:
定义:
- 记录系统活动
- 追踪用户行为
- 安全事件调查
目标:
1. 检测安全事件
2. 调查安全事件
3. 合规性检查
4. 威慑作用
审计内容:
1. 登录和注销
2. 文件访问
3. 权限变更
4. 系统配置修改
5. 异常行为
6. 安全事件
审计日志
审计日志:
内容:
每条日志包含:
- 时间戳
- 用户ID
- 操作类型
- 操作对象
- 操作结果
- IP地址
- 其他信息
日志示例:
2024-01-15 10:30:25 user1 login success 192.168.1.100
2024-01-15 10:31:10 user1 read /home/user1/file.txt success
2024-01-15 10:32:05 user1 write /home/user1/file.txt success
2024-01-15 10:35:20 user1 logout success
日志存储:
- 安全存储
- 防止篡改
- 定期备份
- 长期保存
日志保护:
1. 只追加:
- 只能追加,不能修改
- 防止篡改
2. 加密存储:
- 日志加密
- 防止泄露
3. 访问控制:
- 限制访问
- 只有管理员可以查看
4. 完整性检查:
- 定期检查完整性
- 发现篡改
审计分析
审计分析:
实时分析:
- 实时监控日志
- 检测异常行为
- 及时告警
离线分析:
- 定期分析日志
- 发现模式
- 生成报告
异常检测:
1. 异常登录:
- 异常时间登录
- 异常地点登录
- 多次失败登录
2. 异常访问:
- 访问未授权资源
- 大量文件访问
- 异常操作模式
3. 权限提升:
- 权限变更
- 特权操作
- 可疑活动
报告:
- 安全事件报告
- 合规性报告
- 统计分析报告
审计机制的实施
审计机制实施:
系统级审计:
- 操作系统记录系统事件
- 内核审计
- 系统调用审计
应用级审计:
- 应用程序记录应用事件
- 业务逻辑审计
- 用户行为审计
网络审计:
- 网络流量记录
- 网络连接审计
- 入侵检测
集中审计:
- 集中收集日志
- 统一分析
- 便于管理
工具:
- syslog(Unix/Linux)
- Event Viewer(Windows)
- 第三方审计工具
8.3.4 安全内核
安全内核是操作系统中负责安全的核心部分,实现安全策略和机制。
安全内核:
定义:
- 操作系统中负责安全的核心
- 实现安全策略
- 强制安全机制
位置:
- 在操作系统内核中
- 最底层
- 所有访问都经过
功能:
1. 访问控制
2. 身份认证
3. 审计
4. 加密
5. 安全策略实施
安全内核的设计原则
安全内核设计原则:
1. 最小权限:
- 只给必要权限
- 减少攻击面
- 降低风险
2. 完整性:
- 安全机制完整
- 不能绕过
- 强制实施
3. 隔离:
- 安全功能隔离
- 防止干扰
- 独立运行
4. 可验证性:
- 可以验证正确性
- 形式化验证
- 保证安全
5. 最小化:
- 内核尽可能小
- 减少漏洞
- 提高安全性
可信计算基(TCB)
可信计算基(TCB):
定义:
- 系统中负责安全的组件集合
- 包括硬件、软件
- 必须可信
组成:
1. 硬件:
- CPU
- 内存
- 存储设备
2. 软件:
- 操作系统内核
- 安全模块
- 关键服务
3. 固件:
- BIOS/UEFI
- 安全芯片
原则:
- TCB必须可信
- TCB必须最小化
- TCB必须可验证
保护:
- 保护TCB完整性
- 防止TCB被篡改
- 定期验证
安全内核的实现
安全内核实现:
参考监视器(Reference Monitor):
- 所有访问都经过
- 检查访问权限
- 强制安全策略
特点:
1. 完整性:
- 所有访问都检查
- 不能绕过
2. 隔离性:
- 独立运行
- 不能被干扰
3. 可验证性:
- 可以验证正确性
- 形式化验证
实现方式:
1. 内核模块:
- 作为内核模块
- 例如:SELinux
2. 微内核:
- 安全功能在微内核
- 更安全
3. 虚拟机监控器:
- 在VMM层实现
- 隔离更好
例子:
SELinux:
- Linux安全模块
- 强制访问控制
- 安全策略实施
Windows安全子系统:
- Windows安全组件
- 访问控制
- 身份认证
安全机制总结
安全机制总结:
机制 功能 重要性 实现复杂度
─────────────────────────────────────────
身份认证 验证身份 高 中等
加密技术 保护数据 高 中等
审计机制 记录追踪 中 简单
安全内核 强制安全 高 复杂
综合使用:
- 身份认证:第一道防线
- 访问控制:核心机制
- 加密技术:数据保护
- 审计机制:事后分析
- 安全内核:基础保障
现代系统:
- 综合使用多种机制
- 多层次防护
- 纵深防御
- 提高安全性
438

被折叠的 条评论
为什么被折叠?



